diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..681f41a
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a8b6df3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,21 @@
+Changelog
+==========
+
+Release 1.2 *(2019-09-21)*
+----------------------------
+ * Fix for background service < Android 8.0/Oreo
+ * Add option for haptic feedback (vibration) on lock trigger
+ * Reworked animation logic
+ * Fix 'License' and 'Bug report' links on about page
+
+Release 1.1 *(2019-06-25)*
+----------------------------
+ * Animation scaling issues fixed
+ * Stop service correctly to fix memory leak & reduce code
+ * Notification priority changed to low for Nougat and lower
+ * Option added to run service when screen is off to be able to force PIN entry
+ * Gradle & library updates
+
+Release 1.0 *(2019-06-01)*
+----------------------------
+ * Initial release
\ No newline at end of file
diff --git a/README.md b/README.md
index bb951b0..d932c78 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,9 @@
# Private Lock
-[](LICENSE)
+[](LICENSE)
[
](https://f-droid.org/packages/com.wesaphzt.privatelock/)
-[description]
A simple app to automatically lock your phone based on movement force, or the acceleration to be more accurate.
Private Lock can help protect your privacy and security by monitoring the accelerometer in the background and if the threshold is breached, lock the screen.
diff --git a/app/build.gradle b/app/build.gradle
index 951f4aa..3e2080a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 29
defaultConfig {
applicationId "com.wesaphzt.privatelock"
minSdkVersion 17
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
+ targetSdkVersion 29
+ versionCode 3
+ versionName "1.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -22,10 +22,10 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.intuit.sdp:sdp-android:1.0.3'
- implementation 'androidx.preference:preference:1.0.0'
+ implementation 'androidx.preference:preference:1.1.0'
implementation 'com.github.AppIntro:AppIntro:5.1.0'
- implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.0.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 02f9247..8551446 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
diff --git a/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java b/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java
index a6e5b97..8580647 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/MainActivity.java
@@ -22,10 +22,11 @@ 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.view.ViewTreeObserver;
+import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
@@ -36,10 +37,11 @@ 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 java.util.Locale;
+
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;
@@ -54,7 +56,6 @@ public class MainActivity extends AppCompatActivity {
private boolean mInitialized;
private static SensorManager mSensorManager;
private Sensor mAccelerometer;
- private final float NOISE = (float) 2.0;
private SensorEventListener mActiveListener;
@@ -70,22 +71,26 @@ public class MainActivity extends AppCompatActivity {
public static final String PREFS_THRESHOLD = "THRESHOLD";
private CountDownTimer cdTimer;
- private final int cdTimerLength = 2000;
+ private final int cdTimerLength = 1500;
private boolean isRunning = false;
+ private boolean isHit = false;
+
+ //stats
+ private TextView tvLastBreachValue;
+ private TextView tvAvgBreachValue;
+ private TextView tvHighestBreachValue;
+
+ private int triggerCount = 0;
+ private float avgBreachValueTotal = 0;
+ private double highestBreach = 0;
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;
+ CircleAngleAnimation animation;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -126,19 +131,49 @@ public class MainActivity extends AppCompatActivity {
}
});
- //https://stackoverflow.com/questions/29381474/how-to-draw-a-circle-with-animation-in-android-with-circle-size-based-on-a-value
+ //animation
circle = findViewById(R.id.circle);
circle_bg = findViewById(R.id.circle_bg);
+ final RelativeLayout relativeLayout = findViewById(R.id.content_main);
+ final RelativeLayout rlCircle = findViewById(R.id.rlCircle);
+
+ //scale height/width of animation
+ relativeLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+
+ @Override
+ public void onGlobalLayout() {
+ //only want to do this once
+ relativeLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+
+ circle.setRect((circle.getHeight() / 2), (circle.getHeight()) / 2);
+ circle_bg.setRect((circle_bg.getHeight() / 2), (circle_bg.getHeight()) / 2);
+
+ circle.setX((rlCircle.getWidth() - circle.getRect()) / 2);
+ circle_bg.setX((rlCircle.getWidth() - circle.getRect()) / 2);
+ }
+ });
+
+
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 color
+ int circleBgR = 240;
+ int circleBgG = 240;
+ int circleBgB = 240;
+ circle_bg.setColor(circleBgR, circleBgG, circleBgB);
circle_bg.startAnimation(animation);
+ //stats
+ tvLastBreachValue = findViewById(R.id.tvLastBreachValue);
+ tvAvgBreachValue = findViewById(R.id.tvAvgBreachValue);
+ tvHighestBreachValue = findViewById(R.id.tvHighestBreachValue);
+
//shared prefs
try {
mSensitivity = prefs.getInt(PREFS_THRESHOLD, DEFAULT_SENSITIVITY);
@@ -148,12 +183,19 @@ public class MainActivity extends AppCompatActivity {
//timer when lock hit
cdTimer = new CountDownTimer(cdTimerLength, 1000) {
+
public void onTick(long millisUntilFinished) {
isRunning = true;
+ isHit = true;
}
public void onFinish() {
isRunning = false;
+ isHit = false;
circle.setColor(circleDefaultR, circleDefaultG, circleDefaultB);
+ //reset stat variables
+ triggerCount = 0;
+ avgBreachValueTotal = 0;
+ highestBreach = 0;
}
};
@@ -222,6 +264,7 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
//launch service if permission granted
if (requestCode == REQUEST_CODE_ENABLE_ADMIN) {
if(resultCode == Activity.RESULT_OK) {
@@ -270,27 +313,12 @@ public class MainActivity extends AppCompatActivity {
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);
+ //start service intent
+ Intent startIntent = new Intent(context, LockService.class);
+ startIntent.setAction(LockService.ACTION_START_FOREGROUND_SERVICE);
- //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);
- }
+ startLockService(startIntent);
}
@Override
@@ -376,7 +404,10 @@ public class MainActivity extends AppCompatActivity {
//get current version code
int currentVersionCode = BuildConfig.VERSION_CODE;
//get saved version code
+ int DOESNT_EXIST = -1;
int savedVersionCode = DOESNT_EXIST;
+ //first run
+ String PREF_VERSION_CODE_KEY = "VERSION_CODE";
try {
savedVersionCode = this.prefs.getInt(PREF_VERSION_CODE_KEY, DOESNT_EXIST);
} catch (Exception e) {
@@ -411,6 +442,7 @@ public class MainActivity extends AppCompatActivity {
float deltaY = Math.abs(mLastY - y);
float deltaZ = Math.abs(mLastZ - z);
+ float NOISE = (float) 2.0;
if (deltaX < NOISE) deltaX = (float) 0.0;
if (deltaY < NOISE) deltaY = (float) 0.0;
if (deltaZ < NOISE) deltaZ = (float) 0.0;
@@ -419,35 +451,59 @@ public class MainActivity extends AppCompatActivity {
mLastY = y;
mLastZ = z;
- float total = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
+ float total = (float) Math.sqrt((deltaX * deltaX) + (deltaY * deltaY) + (deltaZ * deltaZ));
- int calculatedAngleInt;
+ int calculatedAngleInt = 0;
- if (total <= mSensitivity) {
- //do nothing if timer currently running
+ if (total >= mSensitivity) {
+ //lock screen threshold hit
+
+ stats(total);
+
+ if(!isHit) {
+ CircleAngleAnimation anim = new CircleAngleAnimation(circle, 360);
+ //circle lock color
+ int circleLockR = 88;
+ int circleLockG = 255;
+ int circleLockB = 135;
+ circle.setColor(circleLockR, circleLockG, circleLockB);
+ anim.setDuration(animationDuration);
+ //set lock color
+ circle.startAnimation(anim);
+
+ isHit = true;
+ cdTimer.start();
+ } else {
+ if(!isRunning) {
+ isRunning = true;
+ }
+ }
+ } else {
if(isRunning)
return;
+ if(isHit)
+ return;
+
calculatedAngleInt = Math.round((total / mSensitivity) * 360);
- CircleAngleAnimation animation = new CircleAngleAnimation(circle, calculatedAngleInt);
+ 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();
}
}
}
+ private void stats(float total) {
+ tvLastBreachValue.setText(String.format(Locale.ENGLISH, "%.1f", (double) total));
+
+ if(total == 0 || total > highestBreach) {
+ highestBreach = total;
+ }
+ tvHighestBreachValue.setText(String.format(Locale.ENGLISH, "%.1f", highestBreach));
+
+ triggerCount += 1;
+ avgBreachValueTotal += total;
+ tvAvgBreachValue.setText(String.format(Locale.ENGLISH, "%.1f", avgBreachValueTotal / triggerCount));
+ }
}
diff --git a/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java b/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java
index fc914ef..229d5ce 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/animation/Circle.java
@@ -13,15 +13,14 @@ public class Circle extends View {
private static final int START_ANGLE_POINT = 90;
private final Paint paint;
- private final RectF rect;
+ private RectF rect;
private float angle;
+ private final int strokeWidth = 10;
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
- final int strokeWidth = 10;
-
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
@@ -29,8 +28,8 @@ public class Circle extends View {
//circle color (currently set upon instance)
//paint.setColor(Color.BLUE);
- //size
- rect = new RectF(strokeWidth, strokeWidth, 600 + strokeWidth, 600 + strokeWidth);
+ //size (currently set upon instance)
+ //rect = new RectF(strokeWidth, strokeWidth, 230 + strokeWidth, 230 + strokeWidth);
//initial angle (optional)
angle = 0;
@@ -53,4 +52,12 @@ public class Circle extends View {
public void setAngle(float angle) {
this.angle = angle;
}
+
+ public void setRect(int right, int bottom) {
+ this.rect = new RectF(strokeWidth, strokeWidth, right, bottom);
+ }
+
+ public float getRect() {
+ return rect.right;
+ }
}
\ 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
index 67d8a44..d11cc99 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentAbout.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentAbout.java
@@ -21,9 +21,8 @@ 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 LICENSE_URI;
+ private static String BUG_REPORT_URI;
private static String AUTHOR_GITHUB;
@Nullable
@@ -32,6 +31,8 @@ public class FragmentAbout extends Fragment {
View v = inflater.inflate(R.layout.fragment_about, container, false);
GITHUB_URI = getString(R.string.app_github);
+ LICENSE_URI = GITHUB_URI + "/blob/master/LICENSE";
+ BUG_REPORT_URI = GITHUB_URI + "/issues";
AUTHOR_GITHUB = getString(R.string.app_github_dev);
String versionName = "";
diff --git a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java
index 5456de0..5289f55 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/fragments/FragmentSettings.java
@@ -7,7 +7,9 @@ import androidx.annotation.Nullable;
import android.view.Menu;
import android.view.View;
+import android.widget.Toast;
+import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
@@ -15,17 +17,26 @@ import com.wesaphzt.privatelock.R;
public class FragmentSettings extends PreferenceFragmentCompat {
- private SharedPreferences prefs;
+ private Context context;
+
+ private CheckBoxPreference cbRunConstant;
+
+ private SharedPreferences sharedPreferences;
+ private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
setHasOptionsMenu(true);
- Context context = getContext();
+ context = getContext();
+
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
//this static call will reset default values only on the first read
PreferenceManager.setDefaultValues(context, R.xml.preferences, false);
+
+ cbRunConstant = (CheckBoxPreference) findPreference("RUN_CONSTANT");
}
@Override
@@ -33,6 +44,24 @@ public class FragmentSettings extends PreferenceFragmentCompat {
super.onCreate(savedInstanceState);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
+ }
+
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -41,6 +70,16 @@ public class FragmentSettings extends PreferenceFragmentCompat {
//bg color
view.setBackgroundColor(getResources().getColor(R.color.white));
+
+ sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if(key.equals("RUN_CONSTANT") && cbRunConstant.isChecked()) {
+ Toast.makeText(context, getString(R.string.settings_restart_service_toast), Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+ sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
}
@Override
diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java
index 7c27e60..d2babb0 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/BootReceiver.java
@@ -8,7 +8,6 @@ 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 {
@@ -16,19 +15,16 @@ public class BootReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);;
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if(prefs.getBoolean("START_ON_BOOT", false)) {
- Intent i = new Intent(context, LockService.class);
+ Intent startIntent = new Intent(context, LockService.class);
+ startIntent.setAction(LockService.ACTION_START_FOREGROUND_SERVICE);
//check android api
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- context.startForegroundService(i);
- LockWidgetProvider lockWidgetProvider = new LockWidgetProvider();
- lockWidgetProvider.setWidgetStart(context);
+ context.startForegroundService(startIntent);
} else {
- context.startService(i);
- LockWidgetProvider lockWidgetProvider = new LockWidgetProvider();
- lockWidgetProvider.setWidgetStart(context);
+ context.startService(startIntent);
}
}
}
diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java
index cb57455..92453ef 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/NotificationReceiver.java
@@ -1,20 +1,11 @@
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 {
@@ -23,37 +14,13 @@ public class NotificationReceiver extends BroadcastReceiver {
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();
+ Intent stopIntent = new Intent(context, LockService.class);
+ stopIntent.setAction(LockService.ACTION_STOP_FOREGROUND_SERVICE);
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);
- }
-
+ context.startForegroundService(stopIntent);
} 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);
- }
+ context.startService(stopIntent);
}
}
}
diff --git a/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java b/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java
index 0aad1e3..02e8331 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/receivers/PresenceReceiver.java
@@ -3,6 +3,8 @@ package com.wesaphzt.privatelock.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
import static com.wesaphzt.privatelock.service.LockService.disabled;
import static com.wesaphzt.privatelock.service.LockService.mInitialized;
@@ -11,6 +13,8 @@ public class PresenceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
//prevent lock animation artifacts
mInitialized = false;
@@ -18,7 +22,9 @@ public class PresenceReceiver extends BroadcastReceiver {
disabled = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- disabled = true;
+ if(! prefs.getBoolean("RUN_CONSTANT", false)) {
+ 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
index 1fbc0f9..3f5adc7 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/service/LockService.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/service/LockService.java
@@ -15,12 +15,15 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
+import android.os.CountDownTimer;
import android.os.IBinder;
import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;
import androidx.core.app.NotificationCompat;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.widget.Toast;
@@ -30,7 +33,11 @@ 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 com.wesaphzt.privatelock.widget.LockWidgetProvider;
+import java.util.Objects;
+
+import static androidx.core.app.NotificationCompat.PRIORITY_LOW;
import static androidx.core.app.NotificationCompat.PRIORITY_MIN;
public class LockService extends JobIntentService {
@@ -40,7 +47,6 @@ public class LockService extends JobIntentService {
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;
@@ -66,55 +72,115 @@ public class LockService extends JobIntentService {
public static final int DEFAULT_SENSITIVITY = 10;
public static int SENSITIVITY;
+ public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";
+ public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
+
+ PresenceReceiver presenceReceiver;
+
+ //check to stop multiple triggers
+ boolean isHit = false;
+
+ //vibrate
+ boolean isHaptic = false;
+
@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();
+ if (intent != null) {
+ String action = intent.getAction();
- 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();
+ LockWidgetProvider lockWidgetProvider = new LockWidgetProvider();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- }
- //------------------------------------------------------------------------------------------
- notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ assert action != null;
+ switch (action) {
+ case ACTION_START_FOREGROUND_SERVICE:
+ presenceReceiver = new PresenceReceiver();
- //dpm
- mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
- mDeviceAdmin = new ComponentName(this, DeviceAdminReceiver.class);
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(presenceReceiver, intentFilter);
+ //------------------------------------------------------------------------------------------
+ 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);
- //prevent lock animation artifacts
- mInitialized = false;
+ //dpm
+ mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mDeviceAdmin = new ComponentName(this, DeviceAdminReceiver.class);
- mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
- mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ //prevent lock animation artifacts
+ mInitialized = false;
- setSensorListener();
- mSensorManager.registerListener(activeListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
+ mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+ mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- setNotification();
- //------------------------------------------------------------------------------------------
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- //create foreground service
- startForeground(NOTIFICATION_ID, notification);
- disabled = false;
+ 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);
+ lockWidgetProvider.setWidgetStart(context);
+ disabled = false;
+
+ } else {
+ notificationManager.notify(NOTIFICATION_ID, notification);
+ lockWidgetProvider.setWidgetStart(context);
+ disabled = false;
+ }
+
+ break;
+
+ case ACTION_STOP_FOREGROUND_SERVICE:
+ try {
+ mSensorManager.unregisterListener(activeListener);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ unregisterReceiver(presenceReceiver);
+
+ disabled = true;
+ mInitialized = false;
+
+ if (PauseReceiver.isRunning) {
+ PauseReceiver.mCountdown.cancel();
+ PauseReceiver.isRunning = false;
+ }
+
+ try {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ String id = context.getString(R.string.notification_main_channel_id);
+ notificationManager.deleteNotificationChannel(id);
+ } else {
+ notificationManager.cancel(LockService.NOTIFICATION_ID);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ lockWidgetProvider.setWidgetStop(context);
+ stopService(intent);
+
+ break;
+ }
} else {
- notificationManager.notify(NOTIFICATION_ID, notification);
- disabled = false;
+ return LockService.START_REDELIVER_INTENT;
}
- //------------------------------------------------------------------------------------------
- return LockService.START_STICKY;
+ return LockService.START_REDELIVER_INTENT;
}
@Override
@@ -125,6 +191,11 @@ public class LockService extends JobIntentService {
@Override
protected void onHandleWork(@NonNull Intent intent) { }
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
private void setSensorListener() {
activeListener = new SensorEventListener() {
@Override
@@ -132,6 +203,11 @@ public class LockService extends JobIntentService {
if(LockService.disabled)
return;
+ //check prefs here so options can be changed without service restart
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ isHaptic = prefs.getBoolean("HAPTIC_FEEDBACK", false);
+
sensorCalc(event);
}
@@ -156,6 +232,7 @@ public class LockService extends JobIntentService {
float deltaY = Math.abs(mLastY - y);
float deltaZ = Math.abs(mLastZ - z);
+ float NOISE = (float) 2.0;
if (deltaX < NOISE) deltaX = (float) 0.0;
if (deltaY < NOISE) deltaY = (float) 0.0;
if (deltaZ < NOISE) deltaZ = (float) 0.0;
@@ -164,18 +241,56 @@ public class LockService extends JobIntentService {
mLastY = y;
mLastZ = z;
- float total = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
+ float total = (float) Math.sqrt((deltaX * deltaX) + (deltaY * deltaY) + (deltaZ * deltaZ));
if (total > SENSITIVITY) {
try {
- mDPM.lockNow();
+ if (isActiveAdmin()) {
+
+ if(!isHaptic) {
+ mDPM.lockNow();
+ } else { //if haptic
+ if(!isHit) { //run if not triggered yet
+ isHit = true;
+
+ mDPM.lockNow();
+ vibrateItBaby();
+
+ //stop multiple haptic triggers in a row
+ new CountDownTimer(1000, 1000) {
+ public void onTick(long millisUntilFinished) {
+ isHit = true;
+ }
+ public void onFinish() {
+ isHit = false;
+ }
+ }.start();
+ }
+ }
+
+ } else {
+ Toast.makeText(context, "Device admin not enabled", Toast.LENGTH_LONG).show();
+ }
} catch (Exception e) {
- Toast.makeText(context, "Error locking, does app have device admin permissions?", Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, "Unknown locking error", Toast.LENGTH_SHORT).show();
}
}
}
}
+ private void vibrateItBaby() {
+ int vibrateTime = 100;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ((Vibrator) Objects.requireNonNull(getSystemService(VIBRATOR_SERVICE))).vibrate(VibrationEffect.createOneShot(vibrateTime, VibrationEffect.DEFAULT_AMPLITUDE));
+ } else {
+ ((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(vibrateTime);
+ }
+ }
+
+ private boolean isActiveAdmin() {
+ return mDPM.isAdminActive(mDeviceAdmin);
+ }
+
private void setNotification() {
//notification
pendingIntent = PendingIntent.getActivity(context, 0,
@@ -206,6 +321,7 @@ public class LockService extends JobIntentService {
.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)
+ .setPriority(PRIORITY_LOW)
.setOngoing(true)
.build();
@@ -221,6 +337,7 @@ public class LockService extends JobIntentService {
.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)
+ .setPriority(PRIORITY_LOW)
.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
index 0f973a6..8fe1ecb 100644
--- a/app/src/main/java/com/wesaphzt/privatelock/widget/LockWidgetProvider.java
+++ b/app/src/main/java/com/wesaphzt/privatelock/widget/LockWidgetProvider.java
@@ -1,6 +1,5 @@
package com.wesaphzt.privatelock.widget;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@@ -14,12 +13,6 @@ 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 {
@@ -72,33 +65,14 @@ public class LockWidgetProvider extends AppWidgetProvider {
editor.putBoolean(context.getString(R.string.widget_prefs_service_id), false);
editor.apply();
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ Intent stopIntent = new Intent(context, LockService.class);
+ stopIntent.setAction(LockService.ACTION_STOP_FOREGROUND_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);
- }
+ context.startForegroundService(stopIntent);
+
} 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);
- }
+ context.startService(stopIntent);
}
//if service is not running
@@ -106,15 +80,13 @@ public class LockWidgetProvider extends AppWidgetProvider {
editor.putBoolean(context.getString(R.string.widget_prefs_service_id), true);
editor.commit();
- Intent i = new Intent(context, LockService.class);
+ Intent startIntent = new Intent(context, LockService.class);
+ startIntent.setAction(LockService.ACTION_START_FOREGROUND_SERVICE);
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- disabled = false;
- context.startForegroundService(i);
- setWidgetStart(context);
+ context.startForegroundService(startIntent);
} else {
- disabled = false;
- context.startService(i);
- setWidgetStart(context);
+ context.startService(startIntent);
}
}
}
diff --git a/app/src/main/res/drawable/ic_intro_lock.xml b/app/src/main/res/drawable/ic_intro_lock.xml
index 6cf40c7..eaf2dbf 100644
--- a/app/src/main/res/drawable/ic_intro_lock.xml
+++ b/app/src/main/res/drawable/ic_intro_lock.xml
@@ -1,24 +1,10 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index 87ea85d..ac201b4 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -141,23 +141,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:gravity="center_horizontal" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3372656..967c9fc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,13 +15,20 @@
→
Value: %1$s
+
+ Last Breach Value:
+ Avg. Breach Value:
+ Highest Breach Value:
+ 0
+
- Private Lock
+ @string/app_name
Private Lock, the perfect companion to help protect your phone privacy and security.
- Protect against theft
+ 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.
+ Always Running
+ Should something happen, your phone will be quickly locked, dependent on the sensitivity you\'ve set.
+ \n\nDon\'t forget to disable battery optimization!
Start
@@ -61,6 +68,9 @@
Bug report image
+
+ If service is running, restart for setting to take effect.
+
Bitcoin image
Litecoin image
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 4ef18b6..b9ad5ab 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -2,20 +2,31 @@
+
+
+
+
+
+
+
-
+ android:title="Pause length" />
-
-
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index ab373f6..171c15e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.android.tools.build:gradle:3.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/fastlane/metadata/android/en-US/changelogs/1.txt b/fastlane/metadata/android/en-US/changelogs/1.txt
index feb0d17..c2eba09 100644
--- a/fastlane/metadata/android/en-US/changelogs/1.txt
+++ b/fastlane/metadata/android/en-US/changelogs/1.txt
@@ -1 +1 @@
-Initial release.
+ * Initial release.
diff --git a/fastlane/metadata/android/en-US/changelogs/2.txt b/fastlane/metadata/android/en-US/changelogs/2.txt
new file mode 100644
index 0000000..481cb52
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/2.txt
@@ -0,0 +1,5 @@
+ * Animation scaling issues fixed
+ * Stop service correctly to fix memory leak & reduce code
+ * Notification priority changed to low for Nougat and lower
+ * Option added to run service when screen is off to be able to force PIN entry
+ * Gradle & library updates
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/3.txt b/fastlane/metadata/android/en-US/changelogs/3.txt
new file mode 100644
index 0000000..3139b55
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/3.txt
@@ -0,0 +1,4 @@
+ * Fix for background service < Android 8.0/Oreo
+ * Add option for haptic feedback (vibration) on lock trigger
+ * Reworked animation logic
+ * Fix 'License' and 'Bug report' links on about page
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index d546dea..024fd7e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,4 +14,9 @@ org.gradle.jvmargs=-Xmx1536m
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
-
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionSha256Sum=53b71812f18cdb2777e9f1b2a0f2038683907c90bdc406bc64d8b400e1fb2c3b
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d8f104c..2fd976f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun May 26 22:24:42 BST 2019
+#Fri Sep 06 22:42:44 BST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip