diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b91807b9d..50942be0c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -122,6 +122,11 @@
android:name=".appshortcuts.AppShortcutLauncherActivity"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
{
if (getActivity() == null) {
return;
}
- final PendingIntent previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE);
+ final PendingIntent previous = SleepTimerUtil.getCurrentTimer(getActivity());
if (previous != null) {
AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
am.cancel(previous);
previous.cancel();
+ getActivity().sendBroadcast(new Intent(MusicService.SLEEP_TIMER_CHANGED));
Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show();
}
@@ -101,7 +106,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
}
})
.showListener(dialog -> {
- if (makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE) != null) {
+ if (SleepTimerUtil.isTimerRunning(getActivity())) {
timerUpdater.start();
}
})
@@ -199,4 +204,8 @@ public void onFinish() {
updateCancelButton();
}
}
+
+ public interface DismissListener {
+ void onDismissed();
+ }
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
index e860f0862..cbbfccc86 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
@@ -98,6 +98,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
public static final String REPEAT_MODE_CHANGED = PHONOGRAPH_PACKAGE_NAME + ".repeatmodechanged";
public static final String SHUFFLE_MODE_CHANGED = PHONOGRAPH_PACKAGE_NAME + ".shufflemodechanged";
public static final String MEDIA_STORE_CHANGED = PHONOGRAPH_PACKAGE_NAME + ".mediastorechanged";
+ public static final String SLEEP_TIMER_CHANGED = PHONOGRAPH_PACKAGE_NAME + ".sleeptimerchanged";
public static final String SAVED_POSITION = "POSITION";
public static final String SAVED_POSITION_IN_TRACK = "POSITION_IN_TRACK";
@@ -169,6 +170,14 @@ public void onReceive(Context context, @NonNull Intent intent) {
}
}
};
+
+ private final BroadcastReceiver sleepTimerReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, @NonNull Intent intent) {
+ updateNotification();
+ }
+ };
+
private ContentObserver mediaStoreObserver;
private boolean notHandledMetaChangedForCurrentTrack;
@@ -203,6 +212,7 @@ public void onCreate() {
uiThreadHandler = new Handler();
registerReceiver(widgetIntentReceiver, new IntentFilter(APP_WIDGET_UPDATE));
+ registerReceiver(sleepTimerReceiver, new IntentFilter(SLEEP_TIMER_CHANGED));
initNotification();
@@ -354,6 +364,7 @@ public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
@Override
public void onDestroy() {
unregisterReceiver(widgetIntentReceiver);
+ unregisterReceiver(sleepTimerReceiver);
if (becomingNoisyReceiverRegistered) {
unregisterReceiver(becomingNoisyReceiver);
becomingNoisyReceiverRegistered = false;
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java
index b17e91ba8..bb9664cb9 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java
@@ -3,11 +3,16 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.service.MusicService;
+import com.kabouzeid.gramophone.ui.activities.MainActivity;
+import com.kabouzeid.gramophone.ui.activities.SleepTimerActivity;
+import com.kabouzeid.gramophone.util.SleepTimerUtil;
import static android.content.Context.NOTIFICATION_SERVICE;
@@ -41,6 +46,24 @@ public synchronized void stop() {
notificationManager.cancel(NOTIFICATION_ID);
}
+ PendingIntent clickAction() {
+ Intent intent = new Intent(service, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return PendingIntent.getActivity(service, 0, intent, 0);
+ }
+
+ PendingIntent playbackAction(final String action) {
+ Intent intent = new Intent(service, MusicService.class).setAction(action);
+ return PendingIntent.getService(service, 0, intent, 0);
+ }
+
+ PendingIntent deleteAction() {
+ return PendingIntent.getService(service, 0, SleepTimerUtil.getTimerAction(service), 0);
+ }
+
+ PendingIntent sleepTimerAction() {
+ return PendingIntent.getActivity(service, 0, new Intent(service, SleepTimerActivity.class), 0);
+ }
+
void updateNotifyModeAndPostNotification(Notification notification) {
int newNotifyMode;
if (service.isPlaying()) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java
index 17065d5fc..08bea9144 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java
@@ -2,12 +2,11 @@
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
@@ -29,11 +28,17 @@
import com.kabouzeid.gramophone.util.ImageUtil;
import com.kabouzeid.gramophone.util.PhonographColorUtil;
import com.kabouzeid.gramophone.util.PreferenceUtil;
+import com.kabouzeid.gramophone.util.SleepTimerUtil;
+
+import java.util.concurrent.TimeUnit;
public class PlayingNotificationImpl extends PlayingNotification {
private Target target;
+ private CountDownTimer countdown;
+ private long timerStopTime;
+
@Override
public synchronized void update() {
stopped = false;
@@ -45,6 +50,38 @@ public synchronized void update() {
final RemoteViews notificationLayout = new RemoteViews(service.getPackageName(), R.layout.notification);
final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(), R.layout.notification_big);
+ if (SleepTimerUtil.isTimerRunning(service)) {
+ long stopTime = PreferenceUtil.getInstance(service).getNextSleepTimerElapsedRealTime();
+
+ if (countdown != null && stopTime != timerStopTime) {
+ countdown.cancel();
+ countdown = null;
+ }
+
+ if (countdown == null) {
+ timerStopTime = stopTime;
+ countdown = new CountDownTimer(timerStopTime - SystemClock.elapsedRealtime(), 60 * 1000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ update();
+ }
+
+ @Override
+ public void onFinish() {
+ countdown = null;
+ }
+ };
+ countdown.start();
+ }
+
+ notificationLayoutBig.setViewVisibility(R.id.sleep_timer, View.VISIBLE);
+ } else if (countdown != null) {
+ countdown.cancel();
+ countdown = null;
+
+ notificationLayoutBig.setViewVisibility(R.id.sleep_timer, View.GONE);
+ }
+
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE);
} else {
@@ -64,15 +101,10 @@ public synchronized void update() {
linkButtons(notificationLayout, notificationLayoutBig);
- Intent action = new Intent(service, MainActivity.class);
- action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- final PendingIntent clickIntent = PendingIntent.getActivity(service, 0, action, 0);
- final PendingIntent deleteIntent = buildPendingIntent(service, MusicService.ACTION_QUIT, null);
-
final Notification notification = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
- .setContentIntent(clickIntent)
- .setDeleteIntent(deleteIntent)
+ .setContentIntent(clickAction())
+ .setDeleteIntent(deleteAction())
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@@ -135,6 +167,7 @@ private void setNotificationContent(boolean dark) {
Bitmap prev = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f);
Bitmap next = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f);
Bitmap playPause = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, primary), 1.5f);
+ Bitmap sleepTimer = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_timer_white_24dp, primary), 1.5f);
notificationLayout.setTextColor(R.id.title, primary);
notificationLayout.setTextColor(R.id.text, secondary);
@@ -145,9 +178,21 @@ private void setNotificationContent(boolean dark) {
notificationLayoutBig.setTextColor(R.id.title, primary);
notificationLayoutBig.setTextColor(R.id.text, secondary);
notificationLayoutBig.setTextColor(R.id.text2, secondary);
+ notificationLayoutBig.setTextColor(R.id.sleep_timer, primary);
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev);
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next);
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause);
+ notificationLayoutBig.setImageViewBitmap(R.id.action_sleep_timer, sleepTimer);
+
+ if (SleepTimerUtil.isTimerRunning(service)) {
+ long millis = PreferenceUtil.getInstance(service).getNextSleepTimerElapsedRealTime() - SystemClock.elapsedRealtime();
+ long minutes = TimeUnit.MINUTES.convert(millis, TimeUnit.MILLISECONDS) + 1;
+ String text = String.format("%d min left", minutes) ;
+ notificationLayoutBig.setTextViewText(R.id.sleep_timer, text);
+ notificationLayoutBig.setViewVisibility(R.id.sleep_timer,View.VISIBLE);
+ } else {
+ notificationLayoutBig.setViewVisibility(R.id.sleep_timer, View.GONE);
+ }
}
});
}
@@ -155,30 +200,21 @@ private void setNotificationContent(boolean dark) {
}
private void linkButtons(final RemoteViews notificationLayout, final RemoteViews notificationLayoutBig) {
- PendingIntent pendingIntent;
-
- final ComponentName serviceName = new ComponentName(service, MusicService.class);
+ PendingIntent previous = playbackAction(MusicService.ACTION_REWIND);
+ notificationLayout.setOnClickPendingIntent(R.id.action_prev, previous);
+ notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, previous);
- // Previous track
- pendingIntent = buildPendingIntent(service, MusicService.ACTION_REWIND, serviceName);
- notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
- notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
+ PendingIntent playPause = playbackAction(MusicService.ACTION_TOGGLE_PAUSE);
+ notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, playPause);
+ notificationLayoutBig.setOnClickPendingIntent(R.id.action_play_pause, playPause);
- // Play and pause
- pendingIntent = buildPendingIntent(service, MusicService.ACTION_TOGGLE_PAUSE, serviceName);
- notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
- notificationLayoutBig.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
-
- // Next track
- pendingIntent = buildPendingIntent(service, MusicService.ACTION_SKIP, serviceName);
- notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent);
- notificationLayoutBig.setOnClickPendingIntent(R.id.action_next, pendingIntent);
- }
+ PendingIntent next = playbackAction(MusicService.ACTION_SKIP);
+ notificationLayout.setOnClickPendingIntent(R.id.action_next, next);
+ notificationLayoutBig.setOnClickPendingIntent(R.id.action_next, next);
- private PendingIntent buildPendingIntent(Context context, final String action, final ComponentName serviceName) {
- Intent intent = new Intent(action);
- intent.setComponent(serviceName);
- return PendingIntent.getService(context, 0, intent, 0);
+ PendingIntent sleepTimer = sleepTimerAction();
+ notificationLayout.setOnClickPendingIntent(R.id.action_sleep_timer, sleepTimer);
+ notificationLayoutBig.setOnClickPendingIntent(R.id.action_sleep_timer, sleepTimer);
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java
index bd0ee315f..903b6043b 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java
@@ -1,15 +1,15 @@
package com.kabouzeid.gramophone.service.notification;
+import android.app.Notification;
import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.media.app.NotificationCompat.MediaStyle;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
import android.support.v7.graphics.Palette;
import com.bumptech.glide.Glide;
@@ -23,11 +23,13 @@
import com.kabouzeid.gramophone.ui.activities.MainActivity;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.PreferenceUtil;
+import com.kabouzeid.gramophone.util.SleepTimerUtil;
import static com.kabouzeid.gramophone.service.MusicService.ACTION_REWIND;
import static com.kabouzeid.gramophone.service.MusicService.ACTION_SKIP;
import static com.kabouzeid.gramophone.service.MusicService.ACTION_TOGGLE_PAUSE;
+@RequiresApi(24)
public class PlayingNotificationImpl24 extends PlayingNotification {
@Override
@@ -42,14 +44,9 @@ public synchronized void update() {
final int playButtonResId = isPlaying
? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp;
- Intent action = new Intent(service, MainActivity.class);
- action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- final PendingIntent clickIntent = PendingIntent.getActivity(service, 0, action, 0);
-
- final ComponentName serviceName = new ComponentName(service, MusicService.class);
- Intent intent = new Intent(MusicService.ACTION_QUIT);
- intent.setComponent(serviceName);
- final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
+ final PendingIntent clickIntent = clickAction();
+ final PendingIntent deleteIntent = deleteAction();
+ PendingIntent sleepTimerIntent = sleepTimerAction();
final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size);
service.runOnUiThread(() -> SongGlideRequest.Builder.from(Glide.with(service), song)
@@ -70,16 +67,21 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) {
void update(Bitmap bitmap, int color) {
if (bitmap == null)
bitmap = BitmapFactory.decodeResource(service.getResources(), R.drawable.default_album_art);
- NotificationCompat.Action playPauseAction = new NotificationCompat.Action(playButtonResId,
+ long time = PreferenceUtil.getInstance(service).getNextSleepTimerElapsedRealTime();
+ boolean sleepTimer = SleepTimerUtil.isTimerRunning(service);
+ Notification.Action playPauseAction = new Notification.Action(playButtonResId,
service.getString(R.string.action_play_pause),
- retrievePlaybackAction(ACTION_TOGGLE_PAUSE));
- NotificationCompat.Action previousAction = new NotificationCompat.Action(R.drawable.ic_skip_previous_white_24dp,
+ playbackAction(ACTION_TOGGLE_PAUSE));
+ Notification.Action previousAction = new Notification.Action(R.drawable.ic_skip_previous_white_24dp,
service.getString(R.string.action_previous),
- retrievePlaybackAction(ACTION_REWIND));
- NotificationCompat.Action nextAction = new NotificationCompat.Action(R.drawable.ic_skip_next_white_24dp,
+ playbackAction(ACTION_REWIND));
+ Notification.Action nextAction = new Notification.Action(R.drawable.ic_skip_next_white_24dp,
service.getString(R.string.action_next),
- retrievePlaybackAction(ACTION_SKIP));
- NotificationCompat.Builder builder = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
+ playbackAction(ACTION_SKIP));
+ Notification.Action sleepTimerAction = new Notification.Action(R.drawable.ic_timer_white_24dp,
+ service.getString(R.string.action_sleep_timer),
+ sleepTimerIntent);
+ Notification.Builder builder = builder()
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(bitmap)
.setContentIntent(clickIntent)
@@ -87,16 +89,18 @@ void update(Bitmap bitmap, int color) {
.setContentTitle(song.title)
.setContentText(text)
.setOngoing(isPlaying)
- .setShowWhen(false)
+ .setShowWhen(sleepTimer)
+ .setUsesChronometer(true)
+ .setChronometerCountDown(true)
+ .setWhen(sleepTimer ? System.currentTimeMillis() + (time - SystemClock.elapsedRealtime()) : 0)
.addAction(previousAction)
.addAction(playPauseAction)
- .addAction(nextAction);
+ .addAction(nextAction)
+ .addAction(sleepTimerAction)
+ .setStyle(new Notification.MediaStyle().setMediaSession((android.media.session.MediaSession.Token)service.getMediaSession().getSessionToken().getToken()).setShowActionsInCompactView(0, 1, 2));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- builder.setStyle(new MediaStyle().setMediaSession(service.getMediaSession().getSessionToken()).setShowActionsInCompactView(0, 1, 2))
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O && PreferenceUtil.getInstance(service).coloredNotification())
- builder.setColor(color);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O && PreferenceUtil.getInstance(service).coloredNotification()) {
+ builder.setColor(color);
}
if (stopped)
@@ -106,10 +110,11 @@ void update(Bitmap bitmap, int color) {
}));
}
- private PendingIntent retrievePlaybackAction(final String action) {
- final ComponentName serviceName = new ComponentName(service, MusicService.class);
- Intent intent = new Intent(action);
- intent.setComponent(serviceName);
- return PendingIntent.getService(service, 0, intent, 0);
+ @NonNull
+ private Notification.Builder builder() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ return new Notification.Builder(service, NOTIFICATION_CHANNEL_ID);
+ }
+ return new Notification.Builder(service);
}
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SleepTimerActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SleepTimerActivity.java
new file mode 100644
index 000000000..4e952cd62
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SleepTimerActivity.java
@@ -0,0 +1,45 @@
+package com.kabouzeid.gramophone.ui.activities;
+
+import android.os.Bundle;
+
+import android.support.annotation.Nullable;
+
+import com.afollestad.materialdialogs.Theme;
+import com.kabouzeid.appthemehelper.ATHActivity;
+import com.kabouzeid.gramophone.R;
+import com.kabouzeid.gramophone.dialogs.SleepTimerDialog;
+import com.kabouzeid.gramophone.util.PreferenceUtil;
+
+
+public class SleepTimerActivity extends ATHActivity implements SleepTimerDialog.DismissListener {
+
+ @Override
+ public void onDismissed() {
+ finish();
+ }
+
+ @Override
+ protected int getThemeRes() {
+ // the SleepTimerDialog theme is chosen by the primary text color of the context. We
+ // therefore have to match the overall theme
+ return (getDialogTheme() == Theme.LIGHT)
+ ? R.style.Activity_Light_Dialog
+ : R.style.Activity_Dark_Dialog;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ new SleepTimerDialog().show(getSupportFragmentManager(), "fragment_dialog");
+ }
+
+ private Theme getDialogTheme() {
+ switch (PreferenceUtil.getInstance(this).getTheme()) {
+ case "dark":
+ case "black":
+ return Theme.DARK;
+ default:
+ return Theme.LIGHT;
+ }
+ }
+}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
index 6eecd2f55..7a57083e1 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
@@ -127,7 +127,11 @@ public void unregisterOnSharedPreferenceChangedListener(SharedPreferences.OnShar
@StyleRes
public int getGeneralTheme() {
- return getThemeResFromPrefValue(mPreferences.getString(GENERAL_THEME, "light"));
+ return getThemeResFromPrefValue(getTheme());
+ }
+
+ public String getTheme() {
+ return mPreferences.getString(GENERAL_THEME, "light");
}
public void setGeneralTheme(String theme) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/SleepTimerUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/SleepTimerUtil.java
new file mode 100644
index 000000000..458b6c7c0
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/SleepTimerUtil.java
@@ -0,0 +1,46 @@
+package com.kabouzeid.gramophone.util;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+
+import com.kabouzeid.gramophone.service.MusicService;
+
+
+public class SleepTimerUtil {
+
+ public static PendingIntent createTimer(Context context) {
+ return makeTimerIntent(context, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ public static Intent getTimerAction(Context context) {
+ return new Intent(context, MusicService.class).setAction(MusicService.ACTION_QUIT);
+ }
+
+ /**
+ * Returns the current sleep timer, if any.
+ * @param context the context.
+ * @return the current timer. Returns {@code null} if the sleep timer is not running.
+ */
+ public static PendingIntent getCurrentTimer(Context context) {
+ return makeTimerIntent(context, PendingIntent.FLAG_NO_CREATE);
+ }
+
+ public static boolean isTimerRunning(Context context) {
+ PendingIntent running = getCurrentTimer(context);
+
+ if (running != null) {
+ // AlarmManager does not seem to cancel intents after the alarm went off. We must
+ // therefore check if it expired to determine whether the sleep timer is actually
+ // running
+ return PreferenceUtil.getInstance(context).getNextSleepTimerElapsedRealTime() - SystemClock.elapsedRealtime() > 0;
+ }
+
+ return false;
+ }
+
+ private static PendingIntent makeTimerIntent(Context context, int flag) {
+ return PendingIntent.getService(context, 110110110, getTimerAction(context), flag);
+ }
+}
diff --git a/app/src/main/res/layout/notification_big.xml b/app/src/main/res/layout/notification_big.xml
index 50428d2f3..5d8f8aebb 100644
--- a/app/src/main/res/layout/notification_big.xml
+++ b/app/src/main/res/layout/notification_big.xml
@@ -14,7 +14,7 @@
~ limitations under the License
-->
-
+
-
-
@@ -66,17 +54,37 @@
android:singleLine="true"
android:textAppearance="@style/Theme.Phonograph.Notification.Title" />
-
+ android:layout_gravity="fill"
+ android:columnCount="2"
+ android:rowCount="1">
+
+
+
+
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 8cb384a3c..c2624b749 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -37,4 +37,13 @@
- toolbar
+
+
+