mirror of
https://github.com/AntennaPod/AntennaPod.git
synced 2025-01-31 10:54:50 +01:00
Vibrate and lower volume when timer is about to expire, shake to reset timer
This commit is contained in:
parent
946d5ef50c
commit
771b1e2a16
@ -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() {
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -404,12 +404,21 @@ public class PlaybackService extends Service {
|
||||
saveCurrentPosition(true, PlaybackServiceTaskManager.POSITION_SAVER_WAITING_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSleepTimerAlmostExpired() {
|
||||
mediaPlayer.setVolume(0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSleepTimerExpired() {
|
||||
mediaPlayer.pause(true, true);
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSleepTimerReset() {
|
||||
mediaPlayer.setVolume(1.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWidgetUpdaterTick() {
|
||||
@ -652,10 +661,9 @@ public class PlaybackService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSleepTimer(long waitingTime) {
|
||||
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
|
||||
+ " milliseconds");
|
||||
taskManager.setSleepTimer(waitingTime);
|
||||
public void setSleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
|
||||
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
|
||||
taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate);
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
|
||||
}
|
||||
|
||||
|
@ -619,6 +619,32 @@ public class PlaybackServiceMediaPlayer {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the playback speed.
|
||||
* This method is executed on an internal executor service.
|
||||
*/
|
||||
public void setVolume(final float volume) {
|
||||
executor.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setVolumeSync(volume);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the playback speed.
|
||||
* This method is executed on the caller's thread.
|
||||
*/
|
||||
private void setVolumeSync(float volume) {
|
||||
playerLock.lock();
|
||||
if (media != null && media.getMediaType() == MediaType.AUDIO) {
|
||||
mediaPlayer.setVolume(volume, volume);
|
||||
Log.d(TAG, "Media player volume was set to " + volume);
|
||||
}
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
||||
public MediaType getCurrentMediaType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package de.danoeh.antennapod.core.service.playback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
@ -14,12 +15,10 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
|
||||
@ -168,7 +167,7 @@ public class PlaybackServiceTaskManager {
|
||||
public synchronized void cancelPositionSaver() {
|
||||
if (isPositionSaverActive()) {
|
||||
positionSaverFuture.cancel(false);
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Cancelled PositionSaver");
|
||||
Log.d(TAG, "Cancelled PositionSaver");
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,9 +185,9 @@ public class PlaybackServiceTaskManager {
|
||||
widgetUpdaterFuture = schedExecutor.scheduleWithFixedDelay(widgetUpdater, WIDGET_UPDATER_NOTIFICATION_INTERVAL,
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Started WidgetUpdater");
|
||||
Log.d(TAG, "Started WidgetUpdater");
|
||||
} else {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Call to startWidgetUpdater was ignored.");
|
||||
Log.d(TAG, "Call to startWidgetUpdater was ignored.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,16 +198,14 @@ public class PlaybackServiceTaskManager {
|
||||
*
|
||||
* @throws java.lang.IllegalArgumentException if waitingTime <= 0
|
||||
*/
|
||||
public synchronized void setSleepTimer(long waitingTime) {
|
||||
public synchronized void setSleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
|
||||
Validate.isTrue(waitingTime > 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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user