diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index edb576249..385201f25 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -121,11 +121,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
countDownLatch.countDown();
}
+ @Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
@Override
public void onSleepTimerExpired() {
}
+ @Override
+ public void onSleepTimerReset() {
+
+ }
+
@Override
public void onWidgetUpdaterTick() {
@@ -169,11 +179,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
+ @Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
@Override
public void onSleepTimerExpired() {
}
+ @Override
+ public void onSleepTimerReset() {
+
+ }
+
@Override
public void onWidgetUpdaterTick() {
countDownLatch.countDown();
@@ -221,7 +241,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startWidgetUpdater();
pstm.startPositionSaver();
- pstm.setSleepTimer(100000);
+ pstm.setSleepTimer(100000, false, false);
pstm.cancelAllTasks();
assertFalse(pstm.isPositionSaverActive());
assertFalse(pstm.isWidgetUpdaterActive());
@@ -240,6 +260,11 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
+ @Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
@Override
public void onSleepTimerExpired() {
if (countDownLatch.getCount() == 0) {
@@ -248,6 +273,11 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
countDownLatch.countDown();
}
+ @Override
+ public void onSleepTimerReset() {
+
+ }
+
@Override
public void onWidgetUpdaterTick() {
@@ -258,7 +288,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
});
- pstm.setSleepTimer(TIME);
+ pstm.setSleepTimer(TIME, false, false);
countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
pstm.shutdown();
}
@@ -274,11 +304,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
+ @Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
@Override
public void onSleepTimerExpired() {
fail("Sleeptimer expired");
}
+ @Override
+ public void onSleepTimerReset() {
+
+ }
+
@Override
public void onWidgetUpdaterTick() {
@@ -289,7 +329,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
});
- pstm.setSleepTimer(TIME);
+ pstm.setSleepTimer(TIME, false, false);
pstm.disableSleepTimer();
assertFalse(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS));
pstm.shutdown();
@@ -298,7 +338,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
public void testIsSleepTimerActivePositive() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
- pstm.setSleepTimer(10000);
+ pstm.setSleepTimer(10000, false, false);
assertTrue(pstm.isSleepTimerActive());
pstm.shutdown();
}
@@ -306,7 +346,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
public void testIsSleepTimerActiveNegative() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
- pstm.setSleepTimer(10000);
+ pstm.setSleepTimer(10000, false, false);
pstm.disableSleepTimer();
assertFalse(pstm.isSleepTimerActive());
pstm.shutdown();
@@ -318,11 +358,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
+ @Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
@Override
public void onSleepTimerExpired() {
}
+ @Override
+ public void onSleepTimerReset() {
+
+ }
+
@Override
public void onWidgetUpdaterTick() {
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index f5b000abf..47bd8b666 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
0, "Waiting time <= 0");
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
- + " milliseconds");
+ Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
if (isSleepTimerActive()) {
sleepTimerFuture.cancel(true);
}
- sleepTimer = new SleepTimer(waitingTime);
+ sleepTimer = new SleepTimer(waitingTime, shakeToReset, vibrate);
sleepTimerFuture = schedExecutor.schedule(sleepTimer, 0, TimeUnit.MILLISECONDS);
}
@@ -216,7 +213,11 @@ public class PlaybackServiceTaskManager {
* Returns true if the sleep timer is currently active.
*/
public synchronized boolean isSleepTimerActive() {
- return sleepTimer != null && sleepTimerFuture != null && !sleepTimerFuture.isCancelled() && !sleepTimerFuture.isDone() && sleepTimer.isWaiting;
+ return sleepTimer != null
+ && sleepTimerFuture != null
+ && !sleepTimerFuture.isCancelled()
+ && !sleepTimerFuture.isDone()
+ && sleepTimer.getWaitingTime() > 0;
}
/**
@@ -224,8 +225,7 @@ public class PlaybackServiceTaskManager {
*/
public synchronized void disableSleepTimer() {
if (isSleepTimerActive()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Disabling sleep timer");
+ Log.d(TAG, "Disabling sleep timer");
sleepTimerFuture.cancel(true);
}
}
@@ -255,7 +255,7 @@ public class PlaybackServiceTaskManager {
public synchronized void cancelWidgetUpdater() {
if (isWidgetUpdaterActive()) {
widgetUpdaterFuture.cancel(false);
- if (BuildConfig.DEBUG) Log.d(TAG, "Cancelled WidgetUpdater");
+ Log.d(TAG, "Cancelled WidgetUpdater");
}
}
@@ -284,16 +284,14 @@ public class PlaybackServiceTaskManager {
Runnable chapterLoader = new Runnable() {
@Override
public void run() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Chapter loader started");
+ Log.d(TAG, "Chapter loader started");
if (media.getChapters() == null) {
media.loadChapterMarks();
if (!Thread.currentThread().isInterrupted() && media.getChapters() != null) {
callback.onChapterLoaded(media);
}
}
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Chapter loader stopped");
+ Log.d(TAG, "Chapter loader stopped");
}
};
chapterLoaderFuture = schedExecutor.submit(chapterLoader);
@@ -324,63 +322,88 @@ public class PlaybackServiceTaskManager {
/**
* Sleeps for a given time and then pauses playback.
*/
- private class SleepTimer implements Runnable {
+ protected class SleepTimer implements Runnable {
private static final String TAG = "SleepTimer";
- private static final long UPDATE_INTERVALL = 1000L;
- private volatile long waitingTime;
- private volatile boolean isWaiting;
+ private static final long UPDATE_INTERVAL = 1000L;
+ private static final long NOTIFICATION_THRESHOLD = 10000;
+ private long waitingTime;
+ private final boolean shakeToReset;
+ private final boolean vibrate;
+ private ShakeListener shakeListener;
- public SleepTimer(long waitingTime) {
+ public SleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
super();
this.waitingTime = waitingTime;
- isWaiting = true;
+ this.shakeToReset = shakeToReset;
+ this.vibrate = vibrate;
}
@Override
public void run() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Starting");
+ Log.d(TAG, "Starting");
+ boolean notifiedAlmostExpired = false;
+ long lastTick = System.currentTimeMillis();
while (waitingTime > 0) {
try {
- Thread.sleep(UPDATE_INTERVALL);
- waitingTime -= UPDATE_INTERVALL;
+ Thread.sleep(UPDATE_INTERVAL);
+ long now = System.currentTimeMillis();
+ waitingTime -= now - lastTick;
+ lastTick = now;
+ Log.d(TAG, "time left: " + waitingTime);
+
+ if(waitingTime < NOTIFICATION_THRESHOLD && !notifiedAlmostExpired) {
+ Log.d(TAG, "Sleep timer is about to expire");
+ if(vibrate) {
+ Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if(v != null) {
+ v.vibrate(500);
+ }
+ }
+ if(shakeListener == null && shakeToReset) {
+ shakeListener = new ShakeListener(context, this);
+ }
+ callback.onSleepTimerAlmostExpired();
+ notifiedAlmostExpired = true;
+ }
if (waitingTime <= 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Waiting completed");
- postExecute();
+ Log.d(TAG, "Sleep timer expired");
+ shakeListener.pause();
+ shakeListener = null;
if (!Thread.currentThread().isInterrupted()) {
callback.onSleepTimerExpired();
}
-
}
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted while waiting");
+ e.printStackTrace();
break;
}
}
- postExecute();
- }
-
- protected void postExecute() {
- isWaiting = false;
}
public long getWaitingTime() {
return waitingTime;
}
- public boolean isWaiting() {
- return isWaiting;
+ public void onShake() {
+ setSleepTimer(15 * 60 * 1000, shakeToReset, vibrate);
+ callback.onSleepTimerReset();
+ shakeListener.pause();
+ shakeListener = null;
}
}
- public static interface PSTMCallback {
+ public interface PSTMCallback {
void positionSaverTick();
+ void onSleepTimerAlmostExpired();
+
void onSleepTimerExpired();
+ void onSleepTimerReset();
+
void onWidgetUpdaterTick();
void onChapterLoaded(Playable media);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java
new file mode 100644
index 000000000..77d765a85
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java
@@ -0,0 +1,63 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.FloatMath;
+import android.util.Log;
+
+public class ShakeListener implements SensorEventListener
+{
+ private static final String TAG = ShakeListener.class.getSimpleName();
+
+ private Sensor mAccelerometer;
+ private SensorManager mSensorMgr;
+ private PlaybackServiceTaskManager.SleepTimer mSleepTimer;
+ private Context mContext;
+
+ public ShakeListener(Context context, PlaybackServiceTaskManager.SleepTimer sleepTimer) {
+ mContext = context;
+ mSleepTimer = sleepTimer;
+ resume();
+ }
+
+ public void resume() {
+ mSensorMgr = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ if (mSensorMgr == null) {
+ throw new UnsupportedOperationException("Sensors not supported");
+ }
+ mAccelerometer = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (!mSensorMgr.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI)) { // if not supported
+ mSensorMgr.unregisterListener(this);
+ throw new UnsupportedOperationException("Accelerometer not supported");
+ }
+ }
+
+ public void pause() {
+ if (mSensorMgr != null) {
+ mSensorMgr.unregisterListener(this);
+ mSensorMgr = null;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float gX = event.values[0] / SensorManager.GRAVITY_EARTH;
+ float gY = event.values[1] / SensorManager.GRAVITY_EARTH;
+ float gZ = event.values[2] / SensorManager.GRAVITY_EARTH;
+
+ float gForce = FloatMath.sqrt(gX*gX + gY*gY + gZ*gZ);
+ if (gForce > 2.25f) {
+ Log.d(TAG, "Detected shake " + gForce);
+ mSleepTimer.onShake();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ return;
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index ba5428b81..42aa3b713 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -652,9 +652,9 @@ public abstract class PlaybackController {
}
}
- public void setSleepTimer(long time) {
+ public void setSleepTimer(long time, boolean shakeToReset, boolean vibrate) {
if (playbackService != null) {
- playbackService.setSleepTimer(time);
+ playbackService.setSleepTimer(time, shakeToReset, vibrate);
}
}