commit fce85eb8420c11d2f0a55d47175f8279a8b74a12 Author: wesaphzt Date: Wed Jul 3 16:06:51 2019 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b75303 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..2996d53 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..37a7509 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..951f4aa --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.wesaphzt.privatelock" + minSdkVersion 17 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.intuit.sdp:sdp-android:1.0.3' + implementation 'androidx.preference:preference:1.0.0' + implementation 'com.github.AppIntro:AppIntro:5.1.0' + + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.material:material:1.0.0' + + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/wesaphzt/privatelock/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/wesaphzt/privatelock/ExampleInstrumentedTest.java new file mode 100644 index 0000000..6c405b7 --- /dev/null +++ b/app/src/androidTest/java/com/wesaphzt/privatelock/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.wesaphzt.privatelock; + +import android.content.Context; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.wesaphzt.privatelock", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..02f9247 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..845b06e Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/com/wesaphzt/privatelock/IntroActivity.java b/app/src/main/java/com/wesaphzt/privatelock/IntroActivity.java new file mode 100644 index 0000000..432e565 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/IntroActivity.java @@ -0,0 +1,72 @@ +package com.wesaphzt.privatelock; + +import android.os.Bundle; +import android.view.WindowManager; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.github.paolorotolo.appintro.AppIntro; +import com.github.paolorotolo.appintro.AppIntroFragment; +import com.github.paolorotolo.appintro.model.SliderPage; + +import static com.wesaphzt.privatelock.service.LockService.mInitialized; + +public class IntroActivity extends AppIntro { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //fullscreen + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + //slider pages + SliderPage sliderPageOne = new SliderPage(); + sliderPageOne.setTitle(getResources().getString(R.string.slider_page_one_title)); + sliderPageOne.setDescription(getString(R.string.slider_page_one_desc)); + sliderPageOne.setImageDrawable(R.drawable.ic_intro_lock); + sliderPageOne.setBgColor(getResources().getColor(R.color.colorPrimary)); + addSlide(AppIntroFragment.newInstance(sliderPageOne)); + + SliderPage sliderPageTwo = new SliderPage(); + sliderPageTwo.setTitle(getResources().getString(R.string.slider_page_two_title)); + sliderPageTwo.setDescription(getResources().getString(R.string.slider_page_two_desc)); + sliderPageTwo.setImageDrawable(R.drawable.ic_intro_iris); + sliderPageTwo.setBgColor(getResources().getColor(R.color.colorIntroGrey)); + addSlide(AppIntroFragment.newInstance(sliderPageTwo)); + + SliderPage sliderPageThree = new SliderPage(); + sliderPageThree.setTitle(getResources().getString(R.string.slider_page_three_title)); + sliderPageThree.setDescription(getResources().getString(R.string.slider_page_three_desc)); + sliderPageThree.setImageDrawable(R.drawable.ic_intro_shield); + sliderPageThree.setBgColor(getResources().getColor(R.color.colorIntroGreen)); + addSlide(AppIntroFragment.newInstance(sliderPageThree)); + + //options + setFadeAnimation(); + showSkipButton(false); + setProgressButtonEnabled(true); + + //setBarColor(getResources().getColor(R.color.colorPrimary)); + //setSeparatorColor(getResources().getColor(R.color.white)); + } + + @Override + public void onSkipPressed(Fragment currentFragment) { + super.onSkipPressed(currentFragment); + } + + @Override + public void onDonePressed(Fragment currentFragment) { + super.onDonePressed(currentFragment); + mInitialized = false; + finish(); + } + + @Override + public void onSlideChanged(@Nullable Fragment oldFragment, @Nullable Fragment newFragment) { + super.onSlideChanged(oldFragment, newFragment); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java b/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java new file mode 100644 index 0000000..a6e5b97 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java @@ -0,0 +1,453 @@ +package com.wesaphzt.privatelock; + +import android.app.Activity; +import android.app.NotificationManager; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import android.os.CountDownTimer; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.wesaphzt.privatelock.animation.Circle; +import com.wesaphzt.privatelock.animation.CircleAngleAnimation; +import com.wesaphzt.privatelock.fragments.FragmentAbout; +import com.wesaphzt.privatelock.fragments.FragmentDonate; +import com.wesaphzt.privatelock.fragments.FragmentSettings; +import com.wesaphzt.privatelock.receivers.DeviceAdminReceiver; +import com.wesaphzt.privatelock.receivers.PauseReceiver; +import com.wesaphzt.privatelock.service.LockService; +import com.wesaphzt.privatelock.widget.LockWidgetProvider; + +import static com.wesaphzt.privatelock.service.LockService.CHANNEL_ID; +import static com.wesaphzt.privatelock.service.LockService.DEFAULT_SENSITIVITY; +import static com.wesaphzt.privatelock.service.LockService.activeListener; +import static com.wesaphzt.privatelock.service.LockService.disabled; + +public class MainActivity extends AppCompatActivity { + + private Context context; + + private int mSensitivity; + private float mLastX, mLastY, mLastZ; + private boolean mInitialized; + private static SensorManager mSensorManager; + private Sensor mAccelerometer; + private final float NOISE = (float) 2.0; + + private SensorEventListener mActiveListener; + + //DevicePolicyManager + private DevicePolicyManager mDPM; + private ComponentName mDeviceAdmin; + + private static final int REQUEST_CODE_ENABLE_ADMIN = 1; + + private TextView tvSensitivityActualValue; + + private SharedPreferences prefs; + public static final String PREFS_THRESHOLD = "THRESHOLD"; + + private CountDownTimer cdTimer; + private final int cdTimerLength = 2000; + private boolean isRunning = false; + + private Circle circle; + private Circle circle_bg; + //circle bg color + private int circleBgR = 240; private int circleBgG = 240; private int circleBgB = 240; + //circle color + private int circleDefaultR = 88; private int circleDefaultG = 186; private int circleDefaultB = 255; + //circle lock color + private int circleLockR = 88; private int circleLockG = 255; private int circleLockB = 135; + int animationDuration = 220; + + //first run + final private String PREF_VERSION_CODE_KEY = "VERSION_CODE"; + final private int DOESNT_EXIST = -1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + context = getApplicationContext(); + this.prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + + getFirstRun(); + + setContentView(R.layout.activity_main); + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + getSupportFragmentManager().addOnBackStackChangedListener( + new FragmentManager.OnBackStackChangedListener() { + public void onBackStackChanged() { + //toggle back arrow on back stack change + if (getSupportActionBar() != null) { + if (getSupportFragmentManager().getBackStackEntryCount() > 0) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } else { + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + //set title + MainActivity.this.setTitle(R.string.app_name); + } + } + } + }); + + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(getSupportFragmentManager().getBackStackEntryCount() > 0){ + getSupportFragmentManager().popBackStack(); + } + } + }); + + //https://stackoverflow.com/questions/29381474/how-to-draw-a-circle-with-animation-in-android-with-circle-size-based-on-a-value + circle = findViewById(R.id.circle); + circle_bg = findViewById(R.id.circle_bg); + + circle.setColor(circleDefaultR, circleDefaultG, circleDefaultB); + //set background circle + CircleAngleAnimation animation = new CircleAngleAnimation(circle_bg, 360); + //initial animation + animation.setDuration(500); + circle_bg.setColor(circleBgR,circleBgG,circleBgB); + + circle_bg.startAnimation(animation); + + //shared prefs + try { + mSensitivity = prefs.getInt(PREFS_THRESHOLD, DEFAULT_SENSITIVITY); + } catch (Exception e) { + Toast.makeText(context, "Unable to retrieve threshold", Toast.LENGTH_LONG).show(); + } + + //timer when lock hit + cdTimer = new CountDownTimer(cdTimerLength, 1000) { + public void onTick(long millisUntilFinished) { + isRunning = true; + } + public void onFinish() { + isRunning = false; + circle.setColor(circleDefaultR, circleDefaultG, circleDefaultB); + } + }; + + //prevent lock animation artifacts + mInitialized = false; + + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + //sensor listener + setSensorListener(); + mSensorManager.registerListener(mActiveListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + + //dpm + mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); + mDeviceAdmin = new ComponentName(this, DeviceAdminReceiver.class); + + tvSensitivityActualValue = findViewById(R.id.tvSensitivityActualValue); + tvSensitivityActualValue.setText(getString(R.string.sensitivity_value, Integer.toString(mSensitivity))); + + //seek bar + SeekBar sbSensitivity = findViewById(R.id.sbSensitivity); + sbSensitivity.setProgress(mSensitivity); + sbSensitivity.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + mSensitivity = progress; + tvSensitivityActualValue.setText(getString(R.string.sensitivity_value, Integer.toString(mSensitivity))); + + //submit to shared prefs + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(PREFS_THRESHOLD, progress).apply(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { } + }); + } + + private void startLockService(Intent intent) { + //check android api + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(intent); + minimizeApp(); + } else { + context.startService(intent); + minimizeApp(); + } + } + + public void minimizeApp() { + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(startMain); + } + + public void requestDeviceAdmin() { + Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdmin); + startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + //launch service if permission granted + if (requestCode == REQUEST_CODE_ENABLE_ADMIN) { + if(resultCode == Activity.RESULT_OK) { + startServicePrep(); + } + } + } + + //determine if we are an active admin + private boolean isActiveAdmin() { + return mDPM.isAdminActive(mDeviceAdmin); + } + + protected void onResume() { + super.onResume(); + mSensorManager.registerListener(mActiveListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + //recreate menu to set start/stop again + invalidateOptionsMenu(); + } + + protected void onPause() { + super.onPause(); + mSensorManager.unregisterListener(mActiveListener); + } + + private void setSensorListener() { + mActiveListener = new SensorEventListener() { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } + + @Override + public void onSensorChanged(SensorEvent event) { + sensorCalc(event); + } + }; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + //inflate actionbar menu + getMenuInflater().inflate(R.menu.menu_main, menu); + + return true; + } + + private void startServicePrep() { + //stop this listener + mSensorManager.unregisterListener(mActiveListener); + Intent intent = new Intent(context, LockService.class); + + try { + //stop any already running service + LockService.mSensorManager.unregisterListener(LockService.activeListener); + startLockService(intent); + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStart(context); + + //cancel any pause timer that might be running + try { + PauseReceiver.mCountdown.cancel(); + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (Exception e) { + startLockService(intent); + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStart(context); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + Fragment fragment = null; + + if (id == R.id.action_start) { + if (isActiveAdmin()) { + startServicePrep(); + } else { + requestDeviceAdmin(); + } + } else if (id == R.id.action_stop) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.deleteNotificationChannel(CHANNEL_ID); + + disabled = true; + mSensorManager.unregisterListener(activeListener); + + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStop(context); + + invalidateOptionsMenu(); + } else { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(LockService.NOTIFICATION_ID); + + disabled = true; + mSensorManager.unregisterListener(activeListener); + + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStop(context); + } + } else if (id == R.id.action_settings) { + fragment = new FragmentSettings(); + } else if (id == R.id.action_donate) { + fragment = new FragmentDonate(); + } else if (id == R.id.action_show_intro) { + Intent myIntent = new Intent(this, IntroActivity.class); + this.startActivity(myIntent); + } else if (id == R.id.action_about) { + fragment = new FragmentAbout(); + } + + //add fragment + if (fragment != null) { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left); + fragmentTransaction.add(R.id.content_main, fragment); + fragmentTransaction.addToBackStack(null); + fragmentTransaction.commit(); + } + + return super.onOptionsItemSelected(item); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem action_start = menu.findItem(R.id.action_start); + MenuItem action_stop = menu.findItem(R.id.action_stop); + + setMenuStatus(action_start, action_stop); + + return true; + } + + public void setMenuStatus(MenuItem action_start, MenuItem action_stop) { + if (disabled) { + action_start.setEnabled(true); + action_stop.setEnabled(false); + } else { + //enabled + action_start.setEnabled(false); + action_stop.setEnabled(true); + } + } + + private void getFirstRun() { + //get current version code + int currentVersionCode = BuildConfig.VERSION_CODE; + //get saved version code + int savedVersionCode = DOESNT_EXIST; + try { + savedVersionCode = this.prefs.getInt(PREF_VERSION_CODE_KEY, DOESNT_EXIST); + } catch (Exception e) { + e.printStackTrace(); + } + //check first run + //noinspection StatementWithEmptyBody + if (currentVersionCode == savedVersionCode) { + //normal run + } else if (savedVersionCode == DOESNT_EXIST) { + //first run + Intent myIntent = new Intent(this, IntroActivity.class); + this.startActivity(myIntent); + } + //update shared prefs with current version code + this.prefs.edit().putInt(PREF_VERSION_CODE_KEY, currentVersionCode).apply(); + } + + private void sensorCalc(SensorEvent event) { + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + + if (!mInitialized) { + mLastX = x; + mLastY = y; + mLastZ = z; + + mInitialized = true; + } else { + float deltaX = Math.abs(mLastX - x); + float deltaY = Math.abs(mLastY - y); + float deltaZ = Math.abs(mLastZ - z); + + if (deltaX < NOISE) deltaX = (float) 0.0; + if (deltaY < NOISE) deltaY = (float) 0.0; + if (deltaZ < NOISE) deltaZ = (float) 0.0; + + mLastX = x; + mLastY = y; + mLastZ = z; + + float total = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ); + + int calculatedAngleInt; + + if (total <= mSensitivity) { + //do nothing if timer currently running + if(isRunning) + return; + + calculatedAngleInt = Math.round((total / mSensitivity) * 360); + + CircleAngleAnimation animation = new CircleAngleAnimation(circle, calculatedAngleInt); + animation.setDuration(animationDuration); + circle.startAnimation(animation); + } else if (total > mSensitivity) { + //lock screen threshold hit + if(isRunning) + //do nothing if timer currently running + return; + + CircleAngleAnimation animation = new CircleAngleAnimation(circle, 360); + animation.setDuration(animationDuration); + //set lock color + circle.setColor(circleLockR, circleLockG, circleLockB); + circle.startAnimation(animation); + + cdTimer.start(); + } + } + } + +} diff --git a/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java b/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java new file mode 100644 index 0000000..fc914ef --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java @@ -0,0 +1,56 @@ +package com.wesaphzt.privatelock.animation; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +public class Circle extends View { + + private static final int START_ANGLE_POINT = 90; + + private final Paint paint; + private final RectF rect; + + private float angle; + + public Circle(Context context, AttributeSet attrs) { + super(context, attrs); + + final int strokeWidth = 10; + + paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.FILL); + paint.setStrokeWidth(strokeWidth); + //circle color (currently set upon instance) + //paint.setColor(Color.BLUE); + + //size + rect = new RectF(strokeWidth, strokeWidth, 600 + strokeWidth, 600 + strokeWidth); + + //initial angle (optional) + angle = 0; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawArc(rect, START_ANGLE_POINT, angle, true, paint); + } + + public float getAngle() { + return angle; + } + + public void setColor(int r, int g, int b) { + paint.setColor(Color.rgb(r, g, b)); + } + + public void setAngle(float angle) { + this.angle = angle; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/animation/CircleAngleAnimation.java b/app/src/main/java/com/wesaphzt/privatelock/animation/CircleAngleAnimation.java new file mode 100644 index 0000000..6a61c9a --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/animation/CircleAngleAnimation.java @@ -0,0 +1,26 @@ +package com.wesaphzt.privatelock.animation; + +import android.view.animation.Animation; +import android.view.animation.Transformation; + +public class CircleAngleAnimation extends Animation { + + private Circle circle; + + private float oldAngle; + private float newAngle; + + public CircleAngleAnimation(Circle circle, int newAngle) { + this.oldAngle = circle.getAngle(); + this.newAngle = newAngle; + this.circle = circle; + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation transformation) { + float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime); + + circle.setAngle(angle); + circle.requestLayout(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentAbout.java b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentAbout.java new file mode 100644 index 0000000..67d8a44 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentAbout.java @@ -0,0 +1,114 @@ +package com.wesaphzt.privatelock.fragments; + +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.wesaphzt.privatelock.R; + +public class FragmentAbout extends Fragment { + + private static String GITHUB_URI; + private static final String LICENSE_URI = GITHUB_URI + "/blob/master/LICENSE.txt"; + private static final String BUG_REPORT_URI = GITHUB_URI + "/issues"; + + private static String AUTHOR_GITHUB; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_about, container, false); + + GITHUB_URI = getString(R.string.app_github); + AUTHOR_GITHUB = getString(R.string.app_github_dev); + + String versionName = ""; + try { + PackageInfo packageInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); + versionName = packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + TextView version = v.findViewById(R.id.about_text_version); + version.setText(versionName); + + LinearLayout license = v.findViewById(R.id.about_layout_license); + LinearLayout source = v.findViewById(R.id.about_layout_source); + + license.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openURI(LICENSE_URI); + } + }); + source.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openURI(GITHUB_URI); + } + }); + + LinearLayout authorLayout = v.findViewById(R.id.aboutLayoutAuthor); + authorLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openURI(AUTHOR_GITHUB); + } + }); + + LinearLayout bugReport = v.findViewById(R.id.about_layout_bugs); + bugReport.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openURI(BUG_REPORT_URI); + } + }); + + return v; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + setHasOptionsMenu(true); + + super.onCreate(savedInstanceState); + } + + private void openURI(String uri) { + try { + Intent openURI = new Intent(Intent.ACTION_VIEW); + openURI.setData(Uri.parse(uri)); + startActivity(openURI); + } catch (Exception e) { + Log.d("app-error", "error opening uri"); + } + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + //hide action bar menu + menu.setGroupVisible(R.id.menu_main, false); + + super.onPrepareOptionsMenu(menu); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //set title + getActivity().setTitle(getString(R.string.fragment_about_title)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentDonate.java b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentDonate.java new file mode 100644 index 0000000..ab1416f --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentDonate.java @@ -0,0 +1,181 @@ +package com.wesaphzt.privatelock.fragments; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import com.wesaphzt.privatelock.R; + +public class FragmentDonate extends Fragment { + + private Context context; + + private static final String BITCOIN_PREFIX = "bitcoin:"; + private static final String LITECOIN_PREFIX = "litecoin:"; + private static final String ETHEREUM_PREFIX = "ethereum:"; + private static final String MONERO_PREFIX = "monero:"; + + private static String BITCOIN_ADDRESS; + private static String BITCOIN_FULL; + + private static String LITECOIN_ADDRESS; + private static String LITECOIN_FULL; + + private static String ETHEREUM_ADDRESS; + private static String ETHEREUM_FULL; + + private static String MONERO_ADDRESS; + private static String MONERO_FULL; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_donate, container, false); + + setHasOptionsMenu(true); + + context = getContext(); + + final TextView tvAddressBtc = view.findViewById(R.id.donate_bitcoin_address); + final TextView tvAddressLtc = view.findViewById(R.id.donate_litecoin_address); + final TextView tvAddressEth = view.findViewById(R.id.donate_ethereum_address); + final TextView tvAddressXmr = view.findViewById(R.id.donate_monero_address); + + BITCOIN_ADDRESS = getString(R.string.donate_bitcoin_address); + BITCOIN_FULL = BITCOIN_PREFIX + BITCOIN_ADDRESS; + tvAddressBtc.setText(BITCOIN_ADDRESS); + + LITECOIN_ADDRESS = getString(R.string.donate_litecoin_address); + LITECOIN_FULL = LITECOIN_PREFIX + LITECOIN_ADDRESS; + tvAddressLtc.setText(LITECOIN_ADDRESS); + + ETHEREUM_ADDRESS = getString(R.string.donate_ethereum_address); + ETHEREUM_FULL = ETHEREUM_PREFIX + ETHEREUM_ADDRESS; + tvAddressEth.setText(ETHEREUM_ADDRESS); + + MONERO_ADDRESS = getString(R.string.donate_monero_address); + MONERO_FULL = MONERO_PREFIX + MONERO_ADDRESS; + tvAddressXmr.setText(MONERO_ADDRESS); + + tvAddressBtc.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //attempt to open bitcoin app, else copy to clipboard + try { + openURI(BITCOIN_FULL); + } catch(Exception ignored) { + copyToClipboard(BITCOIN_ADDRESS); + } + } + }); + tvAddressBtc.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + copyToClipboard(BITCOIN_ADDRESS); + return true; + } + }); + + //litecoin + tvAddressLtc.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //attempt to open litecoin app, else copy to clipboard + try { + openURI(LITECOIN_FULL); + } catch(Exception ignored) { + copyToClipboard(LITECOIN_ADDRESS); + } + } + }); + tvAddressLtc.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + copyToClipboard(LITECOIN_ADDRESS); + return true; + } + }); + + //ethereum + tvAddressEth.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //attempt to open ethereum app, else copy to clipboard + try { + openURI(ETHEREUM_FULL); + } catch(Exception ignored) { + copyToClipboard(ETHEREUM_ADDRESS); + } + } + }); + tvAddressEth.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + copyToClipboard(ETHEREUM_ADDRESS); + return true; + } + }); + + //monero + tvAddressXmr.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //attempt to open ethereum app, else copy to clipboard + try { + openURI(MONERO_FULL); + } catch(Exception ignored) { + copyToClipboard(MONERO_ADDRESS); + } + } + }); + tvAddressXmr.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + copyToClipboard(MONERO_ADDRESS); + return true; + } + }); + + return view; + } + + private void copyToClipboard(String AUTHOR_EXTRA) { + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(getString(R.string.fragment_donate_clipboard_label), AUTHOR_EXTRA); + clipboard.setPrimaryClip(clip); + + Toast.makeText(context, R.string.fragment_donate_clipboard_message, Toast.LENGTH_SHORT).show(); + } + + private void openURI(String uri) { + Intent openURI = new Intent(Intent.ACTION_VIEW); + openURI.setData(Uri.parse(uri)); + startActivity(openURI); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + //hide action bar menu + menu.setGroupVisible(R.id.menu_main, false); + + super.onPrepareOptionsMenu(menu); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //set title + getActivity().setTitle(getString(R.string.fragment_donate_title)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java new file mode 100644 index 0000000..5456de0 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java @@ -0,0 +1,53 @@ +package com.wesaphzt.privatelock.fragments; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import androidx.annotation.Nullable; + +import android.view.Menu; +import android.view.View; + +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; + +import com.wesaphzt.privatelock.R; + +public class FragmentSettings extends PreferenceFragmentCompat { + + private SharedPreferences prefs; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences); + + setHasOptionsMenu(true); + Context context = getContext(); + + //this static call will reset default values only on the first read + PreferenceManager.setDefaultValues(context, R.xml.preferences, false); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + //set title + getActivity().setTitle("Settings"); + + //bg color + view.setBackgroundColor(getResources().getColor(R.color.white)); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + //hide action bar menu + menu.setGroupVisible(R.id.menu_main, false); + + super.onPrepareOptionsMenu(menu); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java new file mode 100644 index 0000000..7c27e60 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java @@ -0,0 +1,36 @@ +package com.wesaphzt.privatelock.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.preference.PreferenceManager; + +import com.wesaphzt.privatelock.service.LockService; +import com.wesaphzt.privatelock.widget.LockWidgetProvider; + +public class BootReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);; + if(prefs.getBoolean("START_ON_BOOT", false)) { + Intent i = new Intent(context, LockService.class); + + //check android api + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(i); + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStart(context); + } else { + context.startService(i); + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStart(context); + } + } + } + } +} diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/DeviceAdminReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/DeviceAdminReceiver.java new file mode 100644 index 0000000..59ced95 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/DeviceAdminReceiver.java @@ -0,0 +1,13 @@ +package com.wesaphzt.privatelock.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class DeviceAdminReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + + } +} diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java new file mode 100644 index 0000000..cb57455 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java @@ -0,0 +1,60 @@ +package com.wesaphzt.privatelock.receivers; + +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.preference.PreferenceManager; + +import com.wesaphzt.privatelock.service.LockService; +import com.wesaphzt.privatelock.widget.LockWidgetProvider; +import com.wesaphzt.privatelock.R; + +import static com.wesaphzt.privatelock.service.LockService.activeListener; +import static com.wesaphzt.privatelock.service.LockService.mSensorManager; +import static com.wesaphzt.privatelock.service.LockService.disabled; + +public class NotificationReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getStringExtra("lock_service"); + + if (action.equals("lock_service_notification")) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String id = context.getString(R.string.notification_main_channel_id); + notificationManager.deleteNotificationChannel(id); + if(PauseReceiver.isRunning) { + PauseReceiver.mCountdown.cancel(); PauseReceiver.isRunning = false; + disabled = true; + mSensorManager.unregisterListener(activeListener); + lockWidgetProvider.setWidgetStop(context); + } else { + disabled = true; + mSensorManager.unregisterListener(activeListener); + lockWidgetProvider.setWidgetStop(context); + } + + } else { + notificationManager.cancel(LockService.NOTIFICATION_ID); + if(PauseReceiver.isRunning) { + PauseReceiver.mCountdown.cancel(); PauseReceiver.isRunning = false; + disabled = true; + mSensorManager.unregisterListener(activeListener); + lockWidgetProvider.setWidgetStop(context); + } else { + disabled = true; + mSensorManager.unregisterListener(activeListener); + lockWidgetProvider.setWidgetStop(context); + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/PauseReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/PauseReceiver.java new file mode 100644 index 0000000..69a6fb4 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/PauseReceiver.java @@ -0,0 +1,67 @@ +package com.wesaphzt.privatelock.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.CountDownTimer; +import android.preference.PreferenceManager; +import android.widget.Toast; + +import com.wesaphzt.privatelock.widget.LockWidgetProvider; + +import static com.wesaphzt.privatelock.service.LockService.disabled; +import static com.wesaphzt.privatelock.service.LockService.mInitialized; + +public class PauseReceiver extends BroadcastReceiver { + + public static CountDownTimer mCountdown; + public static boolean isRunning = false; + + @Override + public void onReceive(final Context context, Intent intent) { + String action = intent.getStringExtra("pause_service"); + + if (action.equals("pause_service_time")) { + //close notification tray + Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + context.sendBroadcast(i); + + if(disabled) { + Toast.makeText(context, "Service is already paused", Toast.LENGTH_SHORT).show(); + return; + } else { + disabled = true; + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetPause(context); + } + + //shared prefs + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int pauseMinuteTime = Integer.parseInt(prefs.getString("PAUSE_TIME", String.valueOf(1))); + int milliPauseTime = pauseMinuteTime * 60 * 1000; + + String plural; + if(pauseMinuteTime == 1) { plural = "minute"; } else { plural = "minutes"; } + + Toast.makeText(context, "Service paused for " + pauseMinuteTime + " " + plural, Toast.LENGTH_LONG).show(); + + mCountdown = new CountDownTimer(milliPauseTime, 1000) { + public void onTick(long millisUntilFinished) { + isRunning = true; + } + public void onFinish() { + //prevent lock animation artifacts + mInitialized = false; + //init + disabled = false; + isRunning = false; + LockWidgetProvider lockWidgetProvider = new LockWidgetProvider(); + lockWidgetProvider.setWidgetStart(context); + + Toast.makeText(context, "Service resumed", Toast.LENGTH_LONG).show(); + } + }.start(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java new file mode 100644 index 0000000..0aad1e3 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java @@ -0,0 +1,24 @@ +package com.wesaphzt.privatelock.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import static com.wesaphzt.privatelock.service.LockService.disabled; +import static com.wesaphzt.privatelock.service.LockService.mInitialized; + +public class PresenceReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) { + //prevent lock animation artifacts + mInitialized = false; + + disabled = false; + + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + disabled = true; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wesaphzt/privatelock/service/LockService.java b/app/src/main/java/com/wesaphzt/privatelock/service/LockService.java new file mode 100644 index 0000000..1fbc0f9 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/service/LockService.java @@ -0,0 +1,251 @@ +package com.wesaphzt.privatelock.service; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Build; +import android.os.IBinder; + +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; +import androidx.core.app.NotificationCompat; + +import android.preference.PreferenceManager; +import android.widget.Toast; + +import com.wesaphzt.privatelock.MainActivity; +import com.wesaphzt.privatelock.R; +import com.wesaphzt.privatelock.receivers.DeviceAdminReceiver; +import com.wesaphzt.privatelock.receivers.NotificationReceiver; +import com.wesaphzt.privatelock.receivers.PauseReceiver; +import com.wesaphzt.privatelock.receivers.PresenceReceiver; + +import static androidx.core.app.NotificationCompat.PRIORITY_MIN; + +public class LockService extends JobIntentService { + + private Context context; + + private float mLastX, mLastY, mLastZ; + public static boolean mInitialized; + public static SensorManager mSensorManager; + private final float NOISE = (float) 2.0; + public static Sensor mAccelerometer; + + public static SensorEventListener activeListener; + public static boolean disabled = true; + + //DevicePolicyManager + DevicePolicyManager mDPM; + ComponentName mDeviceAdmin; + + //notifications + Notification notification; + NotificationManager notificationManager; + //ids + public static String CHANNEL_ID; + public static String CHANNEL_NAME; + public static final int NOTIFICATION_ID = 1000; + //intents + public static PendingIntent pendingIntent; + public static PendingIntent pendingCloseIntent; + public static PendingIntent pendingPauseIntent; + + //sensitivity + public static final int DEFAULT_SENSITIVITY = 10; + public static int SENSITIVITY; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + context = getApplicationContext(); + CHANNEL_ID = getString(R.string.notification_main_channel_id); + CHANNEL_NAME = getString(R.string.notification_main_channel_name); + //------------------------------------------------------------------------------------------ + PresenceReceiver presenceReceiver = new PresenceReceiver(); + + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_USER_PRESENT); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + registerReceiver(presenceReceiver, intentFilter); + //------------------------------------------------------------------------------------------ + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + try { + SENSITIVITY = prefs.getInt(MainActivity.PREFS_THRESHOLD, DEFAULT_SENSITIVITY); + } catch (Exception e) { + Toast.makeText(context, "Unable to retrieve threshold", Toast.LENGTH_LONG).show(); + + } + //------------------------------------------------------------------------------------------ + notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + //dpm + mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); + mDeviceAdmin = new ComponentName(this, DeviceAdminReceiver.class); + + //prevent lock animation artifacts + mInitialized = false; + + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + setSensorListener(); + mSensorManager.registerListener(activeListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + + setNotification(); + //------------------------------------------------------------------------------------------ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + //create foreground service + startForeground(NOTIFICATION_ID, notification); + disabled = false; + + } else { + notificationManager.notify(NOTIFICATION_ID, notification); + disabled = false; + } + //------------------------------------------------------------------------------------------ + + return LockService.START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + protected void onHandleWork(@NonNull Intent intent) { } + + private void setSensorListener() { + activeListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + if(LockService.disabled) + return; + + sensorCalc(event); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { } + }; + } + + private void sensorCalc(SensorEvent event) { + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + + if (!mInitialized) { + mLastX = x; + mLastY = y; + mLastZ = z; + + mInitialized = true; + } else { + float deltaX = Math.abs(mLastX - x); + float deltaY = Math.abs(mLastY - y); + float deltaZ = Math.abs(mLastZ - z); + + if (deltaX < NOISE) deltaX = (float) 0.0; + if (deltaY < NOISE) deltaY = (float) 0.0; + if (deltaZ < NOISE) deltaZ = (float) 0.0; + + mLastX = x; + mLastY = y; + mLastZ = z; + + float total = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ); + + if (total > SENSITIVITY) { + try { + mDPM.lockNow(); + } catch (Exception e) { + Toast.makeText(context, "Error locking, does app have device admin permissions?", Toast.LENGTH_SHORT).show(); + } + } + } + } + + private void setNotification() { + //notification + pendingIntent = PendingIntent.getActivity(context, 0, + new Intent(context, MainActivity.class) + .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP), + 0); + + //notification stop button + Intent intentStopAction = new Intent(context, NotificationReceiver.class); + intentStopAction.putExtra("lock_service","lock_service_notification"); + pendingCloseIntent = PendingIntent.getBroadcast(context,0, intentStopAction, PendingIntent.FLAG_UPDATE_CURRENT); + + //notification pause button + Intent intentPauseAction = new Intent(context, PauseReceiver.class); + intentPauseAction.putExtra("pause_service","pause_service_time"); + pendingPauseIntent = PendingIntent.getBroadcast(context,0, intentPauseAction, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder notificationBuilder; + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { + notificationBuilder = new NotificationCompat.Builder(this); + notification = notificationBuilder + .setSmallIcon(R.drawable.ic_lock_white_24dp) + .setContentTitle(getString(R.string.app_name) + " is running") + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setTicker(getString(R.string.app_name) + " is running") + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "PAUSE", pendingPauseIntent) + .setOngoing(true) + .build(); + + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N| Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { + notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); + notification = notificationBuilder + .setSmallIcon(R.drawable.ic_lock_white_24dp) + .setContentTitle(getString(R.string.app_name) + " is running") + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setColor(getColor(R.color.colorPrimary)) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setTicker(getString(R.string.app_name) + " is running") + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "PAUSE", pendingPauseIntent) + .setOngoing(true) + .build(); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + channel.setImportance(NotificationManager.IMPORTANCE_LOW); + channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + notificationManager.createNotificationChannel(channel); + + notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); + notification = notificationBuilder + .setSmallIcon(R.drawable.ic_lock_white_24dp) + .setContentTitle(getString(R.string.app_name) + " is running") + .setPriority(PRIORITY_MIN) + .setVisibility(NotificationCompat.VISIBILITY_SECRET) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setColor(getColor(R.color.colorPrimary)) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setTicker(getString(R.string.app_name) + " is running") + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "STOP", pendingCloseIntent) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "PAUSE", pendingPauseIntent) + .setOngoing(true) + .build(); + } + } +} diff --git a/app/src/main/java/com/wesaphzt/privatelock/widget/LockWidgetProvider.java b/app/src/main/java/com/wesaphzt/privatelock/widget/LockWidgetProvider.java new file mode 100644 index 0000000..0f973a6 --- /dev/null +++ b/app/src/main/java/com/wesaphzt/privatelock/widget/LockWidgetProvider.java @@ -0,0 +1,186 @@ +package com.wesaphzt.privatelock.widget; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.preference.PreferenceManager; +import android.widget.RemoteViews; + +import com.wesaphzt.privatelock.service.LockService; +import com.wesaphzt.privatelock.R; +import com.wesaphzt.privatelock.receivers.PauseReceiver; + +import static com.wesaphzt.privatelock.service.LockService.CHANNEL_ID; +import static com.wesaphzt.privatelock.service.LockService.activeListener; +import static com.wesaphzt.privatelock.service.LockService.disabled; +import static com.wesaphzt.privatelock.service.LockService.mSensorManager; + +public class LockWidgetProvider extends AppWidgetProvider { + + private static final String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget"; + public boolean SERVICE_STATUS; + + public void onUpdate(Context context, AppWidgetManager appWidgetManager, + int[] appWidgetIds) { + + for (int appWidgetId : appWidgetIds) { + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), + R.layout.app_widget); + + //default status + remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_start_text)); + + Intent intent = new Intent(context, LockWidgetProvider.class); + intent.setAction(ACTION_WIDGET_RECEIVER); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, + 0, intent, 0); + + remoteViews.setOnClickPendingIntent(R.id.llWidget, pendingIntent); + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); + + if (value) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); + editor.apply(); + } + + appWidgetManager.updateAppWidget(appWidgetId, remoteViews); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); + SharedPreferences.Editor editor = prefs.edit(); + + SERVICE_STATUS = value; + + //if service is running + if (SERVICE_STATUS) { + editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); + editor.apply(); + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationManager.deleteNotificationChannel(CHANNEL_ID); + //if countdown timer is running (pause), cancel + if(PauseReceiver.isRunning) { + PauseReceiver.mCountdown.cancel(); PauseReceiver.isRunning = false; + disabled = true; + mSensorManager.unregisterListener(activeListener); + setWidgetStop(context); + } else { + disabled = true; + mSensorManager.unregisterListener(activeListener); + setWidgetStop(context); + } + } else { + notificationManager.cancel(LockService.NOTIFICATION_ID); + if(PauseReceiver.isRunning) { + PauseReceiver.mCountdown.cancel(); PauseReceiver.isRunning = false; + disabled = true; + mSensorManager.unregisterListener(activeListener); + setWidgetStop(context); + } else { + disabled = true; + mSensorManager.unregisterListener(activeListener); + setWidgetStop(context); + } + } + + //if service is not running + } else { + editor.putBoolean(context.getString(R.string.widget_prefs_service_id), true); + editor.commit(); + + Intent i = new Intent(context, LockService.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + disabled = false; + context.startForegroundService(i); + setWidgetStart(context); + } else { + disabled = false; + context.startService(i); + setWidgetStart(context); + } + } + } + super.onReceive(context, intent); + } + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + boolean value = prefs.getBoolean(context.getString(R.string.widget_prefs_service_id), false); + + if (value) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false); + editor.apply(); + } + + super.onDeleted(context, appWidgetIds); + } + + //update widget methods + public void setWidgetStart(Context context) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + prefs.edit().putBoolean(context.getString(R.string.widget_prefs_service_id), true).apply(); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + ComponentName thisWidget = new ComponentName(context, LockWidgetProvider.class); + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget); + + remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_stop_text)); + remoteViews.setImageViewResource(R.id.ivWidgetLock, R.drawable.ic_lock_closed_outline_white_24dp); + remoteViews.setInt(R.id.llWidget, "setBackgroundResource", R.color.colorWidgetStart); + + appWidgetManager.updateAppWidget(thisWidget, remoteViews); + } + + public void setWidgetStop(Context context) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + prefs.edit().putBoolean(context.getString(R.string.widget_prefs_service_id), false).apply(); + + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + ComponentName thisWidget = new ComponentName(context, LockWidgetProvider.class); + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget); + + remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_start_text)); + remoteViews.setImageViewResource(R.id.ivWidgetLock, R.drawable.ic_lock_open_outline_white_24dp); + remoteViews.setInt(R.id.llWidget, "setBackgroundResource", R.color.colorWidgetStop); + + appWidgetManager.updateAppWidget(thisWidget, remoteViews); + } + + public void setWidgetPause(Context context) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(context); + prefs.edit().putBoolean(context.getString(R.string.widget_prefs_service_id), true).apply(); + + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + ComponentName thisWidget = new ComponentName(context, LockWidgetProvider.class); + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.app_widget); + + remoteViews.setTextViewText(R.id.tvWidgetToggle, context.getResources().getString(R.string.widget_stop_text)); + remoteViews.setImageViewResource(R.id.ivWidgetLock, R.drawable.ic_lock_open_outline_white_24dp); + remoteViews.setInt(R.id.llWidget, "setBackgroundResource", R.color.colorWidgetPause); + + appWidgetManager.updateAppWidget(thisWidget, remoteViews); + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/enter_from_left.xml b/app/src/main/res/anim/enter_from_left.xml new file mode 100644 index 0000000..d89d3a5 --- /dev/null +++ b/app/src/main/res/anim/enter_from_left.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/enter_from_right.xml b/app/src/main/res/anim/enter_from_right.xml new file mode 100644 index 0000000..3a49719 --- /dev/null +++ b/app/src/main/res/anim/enter_from_right.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_to_left.xml b/app/src/main/res/anim/exit_to_left.xml new file mode 100644 index 0000000..e683a04 --- /dev/null +++ b/app/src/main/res/anim/exit_to_left.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/exit_to_right.xml b/app/src/main/res/anim/exit_to_right.xml new file mode 100644 index 0000000..e8aecaf --- /dev/null +++ b/app/src/main/res/anim/exit_to_right.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_about_bug_report_black_24dp.xml b/app/src/main/res/drawable/ic_about_bug_report_black_24dp.xml new file mode 100644 index 0000000..4d83902 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_bug_report_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_code_black_24dp.xml b/app/src/main/res/drawable/ic_about_code_black_24dp.xml new file mode 100644 index 0000000..6f1ccb6 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_code_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_copyright_black_24dp.xml b/app/src/main/res/drawable/ic_about_copyright_black_24dp.xml new file mode 100644 index 0000000..fad6892 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_copyright_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_about_info_outline_black_24dp.xml new file mode 100644 index 0000000..cf53e14 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_info_outline_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_about_person_black_24dp.xml b/app/src/main/res/drawable/ic_about_person_black_24dp.xml new file mode 100644 index 0000000..b2cb337 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_person_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_action_settings_white_24dp.xml b/app/src/main/res/drawable/ic_action_settings_white_24dp.xml new file mode 100644 index 0000000..1397d37 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_settings_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_donate_bitcoin_logo.xml b/app/src/main/res/drawable/ic_donate_bitcoin_logo.xml new file mode 100644 index 0000000..4314c27 --- /dev/null +++ b/app/src/main/res/drawable/ic_donate_bitcoin_logo.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_donate_ethereum_logo.xml b/app/src/main/res/drawable/ic_donate_ethereum_logo.xml new file mode 100644 index 0000000..9c27d75 --- /dev/null +++ b/app/src/main/res/drawable/ic_donate_ethereum_logo.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_donate_litecoin_logo.xml b/app/src/main/res/drawable/ic_donate_litecoin_logo.xml new file mode 100644 index 0000000..48356f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_donate_litecoin_logo.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_donate_monero_logo.xml b/app/src/main/res/drawable/ic_donate_monero_logo.xml new file mode 100644 index 0000000..55be9fe --- /dev/null +++ b/app/src/main/res/drawable/ic_donate_monero_logo.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_intro_iris.xml b/app/src/main/res/drawable/ic_intro_iris.xml new file mode 100644 index 0000000..2ee6851 --- /dev/null +++ b/app/src/main/res/drawable/ic_intro_iris.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_intro_lock.xml b/app/src/main/res/drawable/ic_intro_lock.xml new file mode 100644 index 0000000..6cf40c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_intro_lock.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_intro_shield.xml b/app/src/main/res/drawable/ic_intro_shield.xml new file mode 100644 index 0000000..5b9255e --- /dev/null +++ b/app/src/main/res/drawable/ic_intro_shield.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..8036715 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock_closed_outline_white_24dp.xml b/app/src/main/res/drawable/ic_lock_closed_outline_white_24dp.xml new file mode 100644 index 0000000..3d9f543 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_closed_outline_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_lock_open_outline_white_24dp.xml b/app/src/main/res/drawable/ic_lock_open_outline_white_24dp.xml new file mode 100644 index 0000000..6c09b2a --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_open_outline_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_lock_white_24dp.xml b/app/src/main/res/drawable/ic_lock_white_24dp.xml new file mode 100644 index 0000000..146c066 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..9c96440 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/app_widget.xml b/app/src/main/res/layout/app_widget.xml new file mode 100644 index 0000000..d263d1b --- /dev/null +++ b/app/src/main/res/layout/app_widget.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..87ea85d --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 0000000..7254b0e --- /dev/null +++ b/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_donate.xml b/app/src/main/res/layout/fragment_donate.xml new file mode 100644 index 0000000..87dbb4d --- /dev/null +++ b/app/src/main/res/layout/fragment_donate.xml @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..e3a5d3c --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..8c922d6 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..031c178 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ed480f1 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..9830001 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6380d93 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..af807fb Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..120c6b7 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..73a2efa Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f90e58d Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..338a2d3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..515084c --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,21 @@ + + + + + 1 minute + 2 minutes + 3 minutes + 5 minutes + 10 minutes + 15 minutes + + + + 1 + 2 + 3 + 5 + 10 + 15 + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..ef9f303 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,16 @@ + + + #0081dc + #0065ac + #D81B60 + + #FFFFFF + #f3f3f3 + + @color/colorPrimary + #222222 + #ff9600 + + #7e7e7e + #4caf50 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..70d6fed --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + 16dp + 16dp + 8dp + 176dp + 16dp + + + 4dp + 8dp + 16dp + 24dp + 32dp + 40dp + + 14dp + 46dp + + 64dp + diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..6ad2fa3 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #F3F3F3 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..3372656 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,88 @@ + + Private Lock + Open-Source Lock Privacy + + https://github.com/wesaphzt + https://github.com/wesaphzt/PrivateLock + + + Set the lock sensitivity + High + Low + 0 + 40 + Test the lock sensitivity + + Value: %1$s + + + Private Lock + Private Lock, the perfect companion to help protect your phone privacy and security. + Protect against theft + If someone takes your phone while you\'re using it, your phone is unlocked and all your private data is up for grabs. + Always running + Should something happen, your phone will be quickly locked, dependent on the sensitivity you\'ve set. + + + Start + Stop + Settings + Donate + Show Intro + About + + + Donate + Copied address + Address copied to clipboard + About + + + About overview app logo + + Contribute + Report Bugs + Report bugs or request new features + + Author image + wesaphzt + Developer + Developer + + Version + Version image + + License + GPL v3.0 + License image + + Source code + Source code image + + Bug report image + + + Bitcoin image + Litecoin image + Ethereum image + Monero image + + 1GCkvAg9oG79niQTbh6EH9rPALQDXKyHKK + LV687s3wVdhmLZyJMFxomJHdHFXeFAKT5R + 0x785a8804c85b88683a5cce5e53f60878831e5d03 + 43Vijzdt3y42mmT954rSYPjXYabDsjYEV2KyhxfC46JibR2ny9VmRS1fjdJTHxxPVPFE8ajgArwjWfyaRgjh9vcNAwmkfJj + + If you find these apps useful, consider supporting me in some way in my mission to create simple, useful, privacy-oriented, open-source apps. + Text emoji image + + + Start + Stop + + SERVICE_STATUS + Widget padlock image + + + lock_notification_channel_id + Lock Notification Service + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..545b9c6 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + +