From 1ce08c49988b15e1b918caed8009bbcf89d5e0fe Mon Sep 17 00:00:00 2001 From: Samriddha Basu Date: Mon, 13 Mar 2017 05:29:20 +0530 Subject: [PATCH] Switched to Notification.MediaStyle --- .../helper/PlayingNotificationHelper.java | 221 ------------------ .../gramophone/service/MusicService.java | 170 +++++++++++++- app/src/main/res/values/strings.xml | 3 + 3 files changed, 164 insertions(+), 230 deletions(-) delete mode 100644 app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java deleted file mode 100644 index 15e49bc9..00000000 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.kabouzeid.gramophone.helper; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ - -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.Canvas; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; -import android.text.TextUtils; -import android.view.View; -import android.widget.RemoteViews; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.target.Target; -import com.kabouzeid.appthemehelper.util.ColorUtil; -import com.kabouzeid.appthemehelper.util.MaterialValueHelper; -import com.kabouzeid.gramophone.R; -import com.kabouzeid.gramophone.glide.SongGlideRequest; -import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper; -import com.kabouzeid.gramophone.model.Song; -import com.kabouzeid.gramophone.service.MusicService; -import com.kabouzeid.gramophone.ui.activities.MainActivity; -import com.kabouzeid.gramophone.util.PhonographColorUtil; -import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.Util; - -public class PlayingNotificationHelper { - - public static final String TAG = PlayingNotificationHelper.class.getSimpleName(); - private static final int NOTIFICATION_ID = 1; - - private MusicService service; - - private Target target; - - private boolean stopped; - - public PlayingNotificationHelper(@NonNull final MusicService service) { - this.service = service; - } - - public synchronized void updateNotification() { - stopped = false; - - final Song song = service.getCurrentSong(); - if (song.id == -1) { - killNotification(); - return; - } - - final boolean isPlaying = service.isPlaying(); - - final RemoteViews notificationLayout = new RemoteViews(service.getPackageName(), R.layout.notification); - final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(), R.layout.notification_big); - - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { - notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - notificationLayout.setViewVisibility(R.id.media_titles, View.VISIBLE); - notificationLayout.setTextViewText(R.id.title, song.title); - notificationLayout.setTextViewText(R.id.text, song.artistName); - } - - if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils.isEmpty(song.albumName)) { - notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE); - } else { - notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE); - notificationLayoutBig.setTextViewText(R.id.title, song.title); - notificationLayoutBig.setTextViewText(R.id.text, song.artistName); - notificationLayoutBig.setTextViewText(R.id.text2, song.albumName); - } - - linkButtons(notificationLayout, notificationLayoutBig); - - Intent action = new Intent(service, MainActivity.class); - action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - PendingIntent openAppPendingIntent = PendingIntent.getActivity(service, 0, action, 0); - - final Notification notification = new NotificationCompat.Builder(service) - .setSmallIcon(R.drawable.ic_notification) - .setContentIntent(openAppPendingIntent) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setPriority(NotificationCompat.PRIORITY_MAX) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContent(notificationLayout) - .build(); - - notification.bigContentView = notificationLayoutBig; - - final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size); - service.runOnUiThread(new Runnable() { - @Override - public void run() { - if (target != null) { - Glide.clear(target); - } - target = SongGlideRequest.Builder.from(Glide.with(service), song) - .checkIgnoreMediaStore(service) - .generatePalette(service).build() - .into(new SimpleTarget(bigNotificationImageSize, bigNotificationImageSize) { - @Override - public void onResourceReady(BitmapPaletteWrapper resource, GlideAnimation glideAnimation) { - update(resource.getBitmap(), PhonographColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT)); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - super.onLoadFailed(e, errorDrawable); - update(null, Color.TRANSPARENT); - } - - private void update(@Nullable Bitmap bitmap, int bgColor) { - if (bitmap != null) { - notificationLayout.setImageViewBitmap(R.id.image, bitmap); - notificationLayoutBig.setImageViewBitmap(R.id.image, bitmap); - } else { - notificationLayout.setImageViewResource(R.id.image, R.drawable.default_album_art); - notificationLayoutBig.setImageViewResource(R.id.image, R.drawable.default_album_art); - } - - if (!PreferenceUtil.getInstance(service).coloredNotification()) { - bgColor = Color.TRANSPARENT; - } - setBackgroundColor(bgColor); - setNotificationContent(bgColor == Color.TRANSPARENT ? Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP : ColorUtil.isColorLight(bgColor)); - - if (stopped) return; - service.startForeground(NOTIFICATION_ID, notification); - } - - private void setBackgroundColor(int color) { - notificationLayout.setInt(R.id.root, "setBackgroundColor", color); - notificationLayoutBig.setInt(R.id.root, "setBackgroundColor", color); - } - - private void setNotificationContent(boolean dark) { - int primary = MaterialValueHelper.getPrimaryTextColor(service, dark); - int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark); - - Bitmap prev = createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f); - Bitmap next = createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f); - Bitmap playPause = createBitmap(Util.getTintedVectorDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, primary), 1.5f); - Bitmap close = createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, secondary), 1f); - - notificationLayout.setTextColor(R.id.title, primary); - notificationLayout.setTextColor(R.id.text, secondary); - notificationLayout.setImageViewBitmap(R.id.action_prev, prev); - notificationLayout.setImageViewBitmap(R.id.action_next, next); - notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause); - - notificationLayoutBig.setTextColor(R.id.title, primary); - notificationLayoutBig.setTextColor(R.id.text, secondary); - notificationLayoutBig.setTextColor(R.id.text2, secondary); - 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_quit, close); - } - }); - } - }); - - } - - public synchronized void killNotification() { - stopped = true; - service.stopForeground(true); - } - - private void linkButtons(final RemoteViews notificationLayout, final RemoteViews notificationLayoutBig) { - PendingIntent pendingIntent; - - final ComponentName serviceName = new ComponentName(service, MusicService.class); - - // Previous track - pendingIntent = buildPendingIntent(service, MusicService.ACTION_REWIND, serviceName); - notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent); - notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, pendingIntent); - - // 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); - - // Quit - pendingIntent = buildPendingIntent(service, MusicService.ACTION_QUIT, serviceName); - notificationLayoutBig.setOnClickPendingIntent(R.id.action_quit, pendingIntent); - } - - protected 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); - } - - private static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { - Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier), (int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); - drawable.draw(c); - return bitmap; - } -} 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 9884dd04..30745a0b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -1,5 +1,7 @@ package com.kabouzeid.gramophone.service; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; @@ -11,12 +13,15 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; import android.media.audiofx.AudioEffect; +import android.media.session.MediaSession; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -30,6 +35,9 @@ import android.preference.PreferenceManager; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v7.app.NotificationCompat; +import android.text.TextUtils; import android.widget.Toast; import com.bumptech.glide.BitmapRequestBuilder; @@ -42,7 +50,7 @@ import com.kabouzeid.gramophone.appwidgets.AppWidgetClassic; import com.kabouzeid.gramophone.appwidgets.AppWidgetSmall; import com.kabouzeid.gramophone.glide.BlurTransformation; import com.kabouzeid.gramophone.glide.SongGlideRequest; -import com.kabouzeid.gramophone.helper.PlayingNotificationHelper; +import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper; import com.kabouzeid.gramophone.helper.ShuffleHelper; import com.kabouzeid.gramophone.helper.StopWatch; import com.kabouzeid.gramophone.model.Song; @@ -50,6 +58,7 @@ import com.kabouzeid.gramophone.provider.HistoryStore; import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore; import com.kabouzeid.gramophone.provider.SongPlayCountStore; import com.kabouzeid.gramophone.service.Playback.Playback; +import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; import com.kabouzeid.gramophone.util.Util; @@ -128,7 +137,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private boolean queuesRestored; private boolean pausedByTransientLossOfFocus; private boolean receiversAndRemoteControlClientRegistered; - private PlayingNotificationHelper playingNotificationHelper; + private MediaSessionCompat mediaSession; + private NotificationManager notificationManager; + private Notification notification; private AudioManager audioManager; @SuppressWarnings("deprecation") private RemoteControlClient remoteControlClient; @@ -167,8 +178,6 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP public void onCreate() { super.onCreate(); - playingNotificationHelper = new PlayingNotificationHelper(this); - final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName()); wakeLock.setReferenceCounted(false); @@ -191,6 +200,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP registerReceiver(widgetIntentReceiver, new IntentFilter(APP_WIDGET_UPDATE)); + notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + setupMediaSession(); + mediaStoreObserver = new MediaStoreObserver(playerHandler); throttledSeekHandler = new ThrottledSeekHandler(playerHandler); getContentResolver().registerContentObserver( @@ -268,6 +280,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP } } } + return START_STICKY; } @@ -279,6 +292,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP getContentResolver().unregisterContentObserver(mediaStoreObserver); PreferenceUtil.getInstance(this).unregisterOnSharedPreferenceChangedListener(this); wakeLock.release(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mediaSession.release(); + } sendBroadcast(new Intent("com.kabouzeid.gramophone.PHONOGRAPH_MUSIC_SERVICE_DESTROYED")); } @@ -393,7 +409,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private int quit() { unregisterReceiversAndRemoteControlClient(); pause(); - playingNotificationHelper.killNotification(); + notificationManager.cancelAll(); + killNotification(); if (isServiceBound) { return START_STICKY; @@ -953,7 +970,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP switch (what) { case PLAY_STATE_CHANGED: final boolean isPlaying = isPlaying(); - playingNotificationHelper.updateNotification(); + updateNotification(); //noinspection deprecation remoteControlClient.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED); if (!isPlaying && getSongProgressMillis() > 0) { @@ -962,7 +979,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP songPlayCountHelper.notifyPlayStateChanged(isPlaying); break; case META_CHANGED: - playingNotificationHelper.updateNotification(); + updateNotification(); updateRemoteControlClient(); savePosition(); savePositionInTrack(); @@ -978,7 +995,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP if (playingQueue.size() > 0) { prepareNext(); } else { - playingNotificationHelper.killNotification(); + notificationManager.cancelAll(); } break; } @@ -1013,7 +1030,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP updateRemoteControlClient(); break; case PreferenceUtil.COLORED_NOTIFICATION: - playingNotificationHelper.updateNotification(); + updateNotification(); break; } } @@ -1029,6 +1046,141 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP playerHandler.sendEmptyMessage(TRACK_ENDED); } + private void setupMediaSession() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mediaSession = new MediaSessionCompat(this, "Phonograph"); + mediaSession.setCallback(new MediaSessionCompat.Callback() { + @Override + public void onPlay() { + play(); + } + + @Override + public void onPause() { + pause(); + } + + @Override + public void onSkipToNext() { + playNextSong(true); + } + + @Override + public void onSkipToPrevious() { + back(true); + } + + @Override + public void onStop() { + stop(); + } + + }); + + PendingIntent pi = PendingIntent.getBroadcast(this, 0, + new Intent(this, MediaButtonIntentReceiver.class), + PendingIntent.FLAG_UPDATE_CURRENT); + mediaSession.setMediaButtonReceiver(pi); + + mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS + | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); + } + } + + private synchronized void updateNotification() { + final Song song = getCurrentSong(); + if (song.id == -1) { + killNotification(); + return; + } + + final String albumName = song.albumName; + final String artistName = song.artistName; + final boolean isPlaying = isPlaying(); + final String text = TextUtils.isEmpty(albumName) + ? artistName : artistName + " - " + albumName; + + final int playButtonResId = isPlaying + ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; + + Intent action = new Intent(this, MainActivity.class); + action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + final PendingIntent clickIntent = PendingIntent.getActivity(this, 0, action, 0); + + final ComponentName serviceName = new ComponentName(this, MusicService.class); + Intent intent = new Intent(MusicService.ACTION_QUIT); + intent.setComponent(serviceName); + final PendingIntent deleteIntent = PendingIntent.getService(this, 0, intent, 0); + + final BitmapRequestBuilder request = SongGlideRequest.Builder.from(Glide.with(MusicService.this), song) + .checkIgnoreMediaStore(MusicService.this) + .generatePalette(this).build(); + + final int bigNotificationImageSize = getResources().getDimensionPixelSize(R.dimen.notification_big_image_size); + runOnUiThread(new Runnable() { + @Override + public void run() { + request.into(new SimpleTarget(bigNotificationImageSize, bigNotificationImageSize) { + @Override + public void onResourceReady(BitmapPaletteWrapper resource, GlideAnimation glideAnimation) { + update(resource.getBitmap(), resource.getPalette().getDarkVibrantColor(Color.TRANSPARENT)); + } + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + update(null, Color.TRANSPARENT); + } + + void update(Bitmap bitmap, int color) { + if (bitmap == null) bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.default_album_art); + NotificationCompat.Action playPauseAction = new NotificationCompat.Action(playButtonResId, + getString(R.string.action_play_pause), + retrievePlaybackAction(ACTION_TOGGLE_PAUSE)); + NotificationCompat.Action previousAction = new NotificationCompat.Action(R.drawable.ic_skip_previous_white_24dp, + getString(R.string.action_previous), + retrievePlaybackAction(ACTION_REWIND)); + NotificationCompat.Action nextAction = new NotificationCompat.Action(R.drawable.ic_skip_next_white_24dp, + getString(R.string.action_next), + retrievePlaybackAction(ACTION_SKIP)); + NotificationCompat.Builder builder = (NotificationCompat.Builder) new NotificationCompat.Builder(MusicService.this) + .setSmallIcon(R.drawable.ic_notification) + .setLargeIcon(bitmap) + .setContentIntent(clickIntent) + .setDeleteIntent(deleteIntent) + .setContentTitle(song.title) + .setContentText(text) + .setOngoing(isPlaying) + .addAction(previousAction) + .addAction(playPauseAction) + .addAction(nextAction); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.setStyle(new NotificationCompat.MediaStyle().setMediaSession(mediaSession.getSessionToken()).setShowActionsInCompactView(0, 1, 2)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + if (PreferenceUtil.getInstance(MusicService.this).coloredNotification()) builder.setColor(color); + } + + notification = builder.build(); + notificationManager.notify(0, notification); + } + + }); + + } + }); + } + + private PendingIntent retrievePlaybackAction(final String action) { + final ComponentName serviceName = new ComponentName(this, MusicService.class); + Intent intent = new Intent(action); + intent.setComponent(serviceName); + + return PendingIntent.getService(this, 0, intent, 0); + } + + public synchronized void killNotification() { + this.stopForeground(true); + } + private static final class PlaybackHandler extends Handler { @NonNull private final WeakReference mService; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b98dd9a8..7a094d29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,9 @@ Search Play next Play + Play/Pause + Previous + Next Add to playing queue Remove from playing queue Add to playlist…