diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 279ecb5..5ad723f 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
App Usage Data Permission Required
This app requires access to app usage data from your device\'s settings.
+
+ Battery Optimization Enabled
+ This app uses device features that do not function consistently while the app is subject to battery optimizations. Please grant a battery optimization exemption to ensure full and consistent functioning.
diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java
index a90f321..b98ad43 100755
--- a/src/com/audacious_software/passive_data_kit/generators/Generators.java
+++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java
@@ -5,6 +5,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.SparseArray;
@@ -32,6 +33,7 @@ public class Generators {
private HashMap> mGeneratorMap = new HashMap<>();
private SparseArray> mViewTypeMap = new SparseArray<>();
private HashSet mGeneratorUpdatedListeners = new HashSet<>();
+ private HashMap mWakeLocks = new HashMap<>();
public void start() {
if (!this.mStarted)
@@ -257,6 +259,28 @@ public void notifyGeneratorUpdated(String identifier, Bundle bundle) {
}
}
+ public void acquireWakeLock(String tag, int lockType) {
+ this.releaseWakeLock(tag);
+
+ PowerManager power = (PowerManager) this.mContext.getSystemService(Context.POWER_SERVICE);
+
+ PowerManager.WakeLock lock = power.newWakeLock(lockType, tag);
+
+ this.mWakeLocks.put(tag, lock);
+ }
+
+ public void releaseWakeLock(String tag) {
+ if (this.mWakeLocks.containsKey(tag)) {
+ PowerManager.WakeLock lock = this.mWakeLocks.get(tag);
+
+ if (lock.isHeld()) {
+ lock.release();
+ }
+
+ this.mWakeLocks.remove(tag);
+ }
+ }
+
private static class GeneratorsHolder {
public static Generators instance = new Generators();
}
diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java
index c664a3a..85c58de 100755
--- a/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java
+++ b/src/com/audacious_software/passive_data_kit/generators/device/ForegroundApplication.java
@@ -180,7 +180,9 @@ public static ArrayList diagnostics(final Context context) {
actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_usage_stats_permission_required_title), context.getString(R.string.diagnostic_usage_stats_permission_required), new Runnable() {
@Override
public void run() {
- context.startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
+ Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
}
}));
}
diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java
index 823e776..d15e5e8 100755
--- a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java
+++ b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java
@@ -3,6 +3,7 @@
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -14,8 +15,10 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.support.v4.content.ContextCompat;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -52,6 +55,15 @@ public class Accelerometer extends SensorGenerator implements SensorEventListene
private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.Accelerometer.ENABLED";
private static final boolean ENABLED_DEFAULT = true;
+ private static final String IGNORE_POWER_MANAGEMENT = "com.audacious_software.passive_data_kit.generators.sensors.Accelerometer.IGNORE_POWER_MANAGEMENT";
+ private static final boolean IGNORE_POWER_MANAGEMENT_DEFAULT = true;
+
+ private static final String REFRESH_INTERVAL = "com.audacious_software.passive_data_kit.generators.sensors.Accelerometer.REFRESH_INTERVAL";
+ private static final long REFRESH_INTERVAL_DEFAULT = 0;
+
+ private static final String REFRESH_DURATION = "com.audacious_software.passive_data_kit.generators.sensors.Accelerometer.REFRESH_DURATION";
+ private static final long REFRESH_DURATION_DEFAULT = (5 * 60 * 1000);
+
private static final String DATABASE_PATH = "pdk-sensor-accelerometer.sqlite";
private static final int DATABASE_VERSION = 1;
@@ -93,6 +105,7 @@ public class Accelerometer extends SensorGenerator implements SensorEventListene
long mBaseTimestamp = 0;
private long mLatestTimestamp = 0;
+ private Thread mIntervalThread = null;
public static Accelerometer getInstance(Context context) {
if (Accelerometer.sInstance == null) {
@@ -110,6 +123,39 @@ public static void start(final Context context) {
Accelerometer.getInstance(context).startGenerator();
}
+ public void setIgnorePowerManagement(boolean ignore) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+
+ e.putBoolean(Accelerometer.IGNORE_POWER_MANAGEMENT, ignore);
+ e.apply();
+
+ this.stopGenerator();
+ this.startGenerator();
+ }
+
+ public void setRefreshInterval(long interval) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+
+ e.putLong(Accelerometer.REFRESH_INTERVAL, interval);
+ e.apply();
+
+ this.stopGenerator();
+ this.startGenerator();
+ }
+
+ public void setRefreshDuration(long duration) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+
+ e.putLong(Accelerometer.REFRESH_DURATION, duration);
+ e.apply();
+
+ this.stopGenerator();
+ this.startGenerator();
+ }
+
private void startGenerator() {
final SensorManager sensors = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE);
@@ -158,36 +204,95 @@ public void run()
else
sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, Accelerometer.sHandler);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(me.mContext);
+
+ final long refreshInterval = prefs.getLong(Accelerometer.REFRESH_INTERVAL, Accelerometer.REFRESH_INTERVAL_DEFAULT);
+
+ if (refreshInterval > 0) {
+ final long refreshDuration = prefs.getLong(Accelerometer.REFRESH_DURATION, Accelerometer.REFRESH_DURATION_DEFAULT);
+
+ if (me.mIntervalThread != null) {
+ me.mIntervalThread.interrupt();
+ me.mIntervalThread = null;
+ }
+
+ Runnable managerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ Thread.sleep(refreshDuration);
+
+ sensors.unregisterListener(me, me.mSensor);
+
+ Thread.sleep(refreshInterval - refreshDuration);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+ sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, 0, Accelerometer.sHandler);
+ else
+ sensors.registerListener(me, me.mSensor, SensorManager.SENSOR_DELAY_NORMAL, Accelerometer.sHandler);
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ me.mIntervalThread = new Thread(managerRunnable, "accelerometer-interval");
+ me.mIntervalThread.start();
+ }
+
Looper.loop();
}
};
Thread t = new Thread(r, "accelerometer");
t.start();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(me.mContext);
+
+ if (prefs.getBoolean(Accelerometer.IGNORE_POWER_MANAGEMENT, Accelerometer.IGNORE_POWER_MANAGEMENT_DEFAULT)) {
+ Generators.getInstance(this.mContext).acquireWakeLock(Accelerometer.IDENTIFIER, PowerManager.PARTIAL_WAKE_LOCK);
+ } else {
+ Generators.getInstance(this.mContext).releaseWakeLock(Accelerometer.IDENTIFIER);
+ }
} else {
- if (this.mSensor != null) {
- sensors.unregisterListener(this, this.mSensor);
+ this.stopGenerator();
+ }
+ }
- if (Accelerometer.sHandler != null) {
- Looper loop = Accelerometer.sHandler.getLooper();
- loop.quit();
+ private void stopGenerator() {
+ final SensorManager sensors = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE);
- Accelerometer.sHandler = null;
- }
+ if (this.mSensor != null) {
+ if (this.mIntervalThread != null) {
+ this.mIntervalThread.interrupt();
+ this.mIntervalThread = null;
+ }
- me.mXValueBuffers = null;
- me.mYValueBuffers = null;
- me.mZValueBuffers = null;
- me.mAccuracyBuffers = null;
- me.mRawTimestampBuffers = null;
- me.mTimestampBuffers = null;
+ sensors.unregisterListener(this, this.mSensor);
- me.mActiveBuffersIndex = 0;
- me.mCurrentBufferIndex = 0;
+ if (Accelerometer.sHandler != null) {
+ Looper loop = Accelerometer.sHandler.getLooper();
+ loop.quit();
- this.mSensor = null;
+ Accelerometer.sHandler = null;
}
+
+ this.mXValueBuffers = null;
+ this.mYValueBuffers = null;
+ this.mZValueBuffers = null;
+ this.mAccuracyBuffers = null;
+ this.mRawTimestampBuffers = null;
+ this.mTimestampBuffers = null;
+
+ this.mActiveBuffersIndex = 0;
+ this.mCurrentBufferIndex = 0;
+
+ this.mSensor = null;
}
+
+ Generators.getInstance(this.mContext).releaseWakeLock(Accelerometer.IDENTIFIER);
}
public static boolean isEnabled(Context context) {
@@ -204,8 +309,30 @@ public static boolean isRunning(Context context) {
return Accelerometer.sInstance.mSensor != null;
}
- public static ArrayList diagnostics(Context context) {
- return new ArrayList<>();
+ public static ArrayList diagnostics(final Context context) {
+ ArrayList actions = new ArrayList<>();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ if (prefs.getBoolean(Accelerometer.IGNORE_POWER_MANAGEMENT, Accelerometer.IGNORE_POWER_MANAGEMENT_DEFAULT)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ PowerManager power = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
+ if (power.isIgnoringBatteryOptimizations(context.getPackageName()) == false) {
+ actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_battery_optimization_exempt_title), context.getString(R.string.diagnostic_battery_optimization_exempt), new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }));
+ }
+ }
+ }
+
+ return actions;
}
public static void bindViewHolder(final DataPointViewHolder holder) {
@@ -483,12 +610,9 @@ public String getFormattedValue(float value, AxisBase axis) {
dateLabel.setText(R.string.label_never_pdk);
}
-
- Log.e("PDK", "MAIN DONE: " + (System.currentTimeMillis() - drawStart));
}
- public static View fetchView(ViewGroup parent)
- {
+ public static View fetchView(ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_sensors_accelerometer, parent, false);
}
@@ -539,8 +663,6 @@ public void onSensorChanged(SensorEvent sensorEvent) {
}
}
-// Log.e("PDK", "ACCEL[" + this.mCurrentBufferIndex + "/" + this.mActiveBuffersIndex + "] = " + sensorEvent.values[0] + " -- " + sensorEvent.values[1] + " -- " + sensorEvent.values[2]);
-
this.mXValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[0];
this.mYValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[1];
this.mZValueBuffers[this.mActiveBuffersIndex][this.mCurrentBufferIndex] = sensorEvent.values[2];
@@ -602,7 +724,7 @@ public void run() {
if (now - me.mLastCleanup > me.mCleanupInterval) {
me.mLastCleanup = now;
- long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000;
+ long start = (now - (24 * 60 * 60 * 1000)) * 1000 * 1000;
String where = Accelerometer.HISTORY_OBSERVED + " < ?";
String[] args = { "" + start };
diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java
index eee9a3b..145cf6e 100755
--- a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java
+++ b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java
@@ -3,6 +3,7 @@
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -14,6 +15,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
@@ -52,6 +56,9 @@ public class AmbientLight extends SensorGenerator implements SensorEventListener
private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.AmbientLight.ENABLED";
private static final boolean ENABLED_DEFAULT = true;
+ private static final String IGNORE_POWER_MANAGEMENT = "com.audacious_software.passive_data_kit.generators.sensors.AmbientLight.IGNORE_POWER_MANAGEMENT";
+ private static final boolean IGNORE_POWER_MANAGEMENT_DEFAULT = true;
+
private static final String DATABASE_PATH = "pdk-sensor-ambient-light.sqlite";
private static final int DATABASE_VERSION = 1;
@@ -100,6 +107,17 @@ public AmbientLight(Context context) {
super(context);
}
+ public void setIgnorePowerManagement(boolean ignore) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+
+ e.putBoolean(AmbientLight.IGNORE_POWER_MANAGEMENT, ignore);
+ e.apply();
+
+ this.stopGenerator();
+ this.startGenerator();
+ }
+
public static void start(final Context context) {
AmbientLight.getInstance(context).startGenerator();
}
@@ -156,28 +174,44 @@ public void run()
Thread t = new Thread(r, "ambient-light");
t.start();
- } else {
- if (this.mSensor != null) {
- sensors.unregisterListener(this, this.mSensor);
- if (AmbientLight.sHandler != null) {
- Looper loop = AmbientLight.sHandler.getLooper();
- loop.quit();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
- AmbientLight.sHandler = null;
- }
+ if (prefs.getBoolean(AmbientLight.IGNORE_POWER_MANAGEMENT, AmbientLight.IGNORE_POWER_MANAGEMENT_DEFAULT)) {
+ Generators.getInstance(this.mContext).acquireWakeLock(Accelerometer.IDENTIFIER, PowerManager.PARTIAL_WAKE_LOCK);
+ } else {
+ Generators.getInstance(this.mContext).releaseWakeLock(Accelerometer.IDENTIFIER);
+ }
+ } else {
+ this.stopGenerator();
+ }
+ }
- me.mValueBuffers = null;
- me.mAccuracyBuffers = null;
- me.mRawTimestampBuffers = null;
- me.mTimestampBuffers = null;
+ private void stopGenerator() {
+ final SensorManager sensors = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE);
- me.mActiveBuffersIndex = 0;
- me.mCurrentBufferIndex = 0;
+ if (this.mSensor != null) {
+ sensors.unregisterListener(this, this.mSensor);
- this.mSensor = null;
+ if (AmbientLight.sHandler != null) {
+ Looper loop = AmbientLight.sHandler.getLooper();
+ loop.quit();
+
+ AmbientLight.sHandler = null;
}
+
+ this.mValueBuffers = null;
+ this.mAccuracyBuffers = null;
+ this.mRawTimestampBuffers = null;
+ this.mTimestampBuffers = null;
+
+ this.mActiveBuffersIndex = 0;
+ this.mCurrentBufferIndex = 0;
+
+ this.mSensor = null;
}
+
+ Generators.getInstance(this.mContext).releaseWakeLock(AmbientLight.IDENTIFIER);
}
public static boolean isEnabled(Context context) {
@@ -194,8 +228,30 @@ public static boolean isRunning(Context context) {
return AmbientLight.sInstance.mSensor != null;
}
- public static ArrayList diagnostics(Context context) {
- return new ArrayList<>();
+ public static ArrayList diagnostics(final Context context) {
+ ArrayList actions = new ArrayList<>();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ if (prefs.getBoolean(AmbientLight.IGNORE_POWER_MANAGEMENT, AmbientLight.IGNORE_POWER_MANAGEMENT_DEFAULT)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ PowerManager power = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
+ if (power.isIgnoringBatteryOptimizations(context.getPackageName()) == false) {
+ actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_battery_optimization_exempt_title), context.getString(R.string.diagnostic_battery_optimization_exempt), new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }));
+ }
+ }
+ }
+
+ return actions;
}
public static void bindViewHolder(final DataPointViewHolder holder) {
@@ -508,7 +564,7 @@ public void run() {
if (now - me.mLastCleanup > me.mCleanupInterval) {
me.mLastCleanup = now;
- long start = (now - (2 * 24 * 60 * 60 * 1000)) * 1000 * 1000;
+ long start = (now - (24 * 60 * 60 * 1000)) * 1000 * 1000;
String where = AmbientLight.HISTORY_OBSERVED + " < ?";
String[] args = { "" + start };
diff --git a/src/com/audacious_software/passive_data_kit/generators/wearables/WithingsDevice.java b/src/com/audacious_software/passive_data_kit/generators/wearables/WithingsDevice.java
index b56eeb9..0d09ce2 100755
--- a/src/com/audacious_software/passive_data_kit/generators/wearables/WithingsDevice.java
+++ b/src/com/audacious_software/passive_data_kit/generators/wearables/WithingsDevice.java
@@ -1126,6 +1126,7 @@ public void run() {
builder.appendQueryParameter("oauth_signature", signature.trim());
Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (NoSuchAlgorithmException e) {
AppEvent.getInstance(context).logThrowable(e);