From b47f6e8f3c67adceeefd25ae8eef68367415c155 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Fri, 17 May 2019 15:16:22 +0200 Subject: [PATCH] Moved notification setup to new class --- .../service/playback/PlaybackService.java | 153 ++------------- .../PlaybackServiceNotificationBuilder.java | 184 ++++++++++++++++++ 2 files changed, 197 insertions(+), 140 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index 277d072fe..8395c227c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -89,7 +89,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { /** * True if cast session should disconnect. */ - private static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect"; + public static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect"; /** * True if media should be streamed. */ @@ -264,7 +264,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { Log.d(TAG, "Service created."); isRunning = true; - NotificationCompat.Builder notificationBuilder = createBasicNotification(); + PlaybackServiceNotificationBuilder notificationBuilder = new PlaybackServiceNotificationBuilder(this); startForeground(NOTIFICATION_ID, notificationBuilder.build()); registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS")); @@ -320,24 +320,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_STARTED)); } - private NotificationCompat.Builder createBasicNotification() { - final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()); - - final PendingIntent pIntent = PendingIntent.getActivity(this, 0, - PlaybackService.getPlayerActivityIntent(this), - PendingIntent.FLAG_UPDATE_CURRENT); - - return new NotificationCompat.Builder( - this, NotificationUtils.CHANNEL_ID_PLAYING) - .setContentTitle(getString(R.string.app_name)) - .setContentText("Service is running") // Just in case the notification is not updated (should not occur) - .setOngoing(false) - .setContentIntent(pIntent) - .setWhen(0) // we don't need the time - .setSmallIcon(smallIcon) - .setPriority(NotificationCompat.PRIORITY_MIN); - } - @Override public void onDestroy() { super.onDestroy(); @@ -1227,8 +1209,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { return; } Runnable notificationSetupTask = new Runnable() { - Bitmap icon = null; - @Override public void run() { Log.d(TAG, "Starting background work"); @@ -1240,115 +1220,20 @@ public class PlaybackService extends MediaBrowserServiceCompat { } return; } - - int iconSize = getResources().getDimensionPixelSize( - android.R.dimen.notification_large_icon_width); - try { - icon = Glide.with(PlaybackService.this) - .asBitmap() - .load(playable.getImageLocation()) - .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY)) - .apply(new RequestOptions().centerCrop()) - .submit(iconSize, iconSize) - .get(); - } catch (Throwable tr) { - Log.e(TAG, "Error loading the media icon for the notification", tr); - } - - if (icon == null) { - icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), - ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext())); - } - PlayerStatus playerStatus = mediaPlayer.getPlayerStatus(); + PlaybackServiceNotificationBuilder notificationBuilder = + new PlaybackServiceNotificationBuilder(PlaybackService.this); + notificationBuilder.addMetadata(playable, mediaSession.getSessionToken(), playerStatus, isCasting); + + if (!notificationBuilder.isIconCached(playable)) { + // To make sure that the notification is shown instantly + notificationBuilder.loadDefaultIcon(); + startForeground(NOTIFICATION_ID, notificationBuilder.build()); + } + notificationBuilder.loadIcon(playable); if (!Thread.currentThread().isInterrupted() && started) { - String contentText = playable.getEpisodeTitle(); - String contentTitle = playable.getFeedTitle(); - Notification notification; - - // Builder is v7, even if some not overwritten methods return its parent's v4 interface - NotificationCompat.Builder notificationBuilder = createBasicNotification(); - notificationBuilder.setContentTitle(contentTitle) - .setContentText(contentText) - .setPriority(UserPreferences.getNotifyPriority()) - .setLargeIcon(icon); // set notification priority - IntList compactActionList = new IntList(); - - int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction - - if (isCasting) { - Intent stopCastingIntent = new Intent(PlaybackService.this, PlaybackService.class); - stopCastingIntent.putExtra(EXTRA_CAST_DISCONNECT, true); - PendingIntent stopCastingPendingIntent = PendingIntent.getService(PlaybackService.this, - numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT); - notificationBuilder.addAction(R.drawable.ic_media_cast_disconnect, - getString(R.string.cast_disconnect_label), - stopCastingPendingIntent); - numActions++; - } - - // always let them rewind - PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_REWIND, numActions); - notificationBuilder.addAction(android.R.drawable.ic_media_rew, - getString(R.string.rewind_label), - rewindButtonPendingIntent); - if (UserPreferences.showRewindOnCompactNotification()) { - compactActionList.add(numActions); - } - numActions++; - - if (playerStatus == PlayerStatus.PLAYING) { - PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_PAUSE, numActions); - notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action - getString(R.string.pause_label), - pauseButtonPendingIntent); - compactActionList.add(numActions++); - } else { - PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_PLAY, numActions); - notificationBuilder.addAction(android.R.drawable.ic_media_play, //play action - getString(R.string.play_label), - playButtonPendingIntent); - compactActionList.add(numActions++); - } - - // ff follows play, then we have skip (if it's present) - PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions); - notificationBuilder.addAction(android.R.drawable.ic_media_ff, - getString(R.string.fast_forward_label), - ffButtonPendingIntent); - if (UserPreferences.showFastForwardOnCompactNotification()) { - compactActionList.add(numActions); - } - numActions++; - - if (UserPreferences.isFollowQueue()) { - PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_NEXT, numActions); - notificationBuilder.addAction(android.R.drawable.ic_media_next, - getString(R.string.skip_episode_label), - skipButtonPendingIntent); - if (UserPreferences.showSkipOnCompactNotification()) { - compactActionList.add(numActions); - } - numActions++; - } - - PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction( - KeyEvent.KEYCODE_MEDIA_STOP, numActions); - notificationBuilder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle() - .setMediaSession(mediaSession.getSessionToken()) - .setShowActionsInCompactView(compactActionList.toArray()) - .setShowCancelButton(true) - .setCancelButtonIntent(stopButtonPendingIntent)) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(NotificationCompat.COLOR_DEFAULT); - - notification = notificationBuilder.build(); + Notification notification = notificationBuilder.build(); if (playerStatus == PlayerStatus.PLAYING || playerStatus == PlayerStatus.PREPARING || @@ -1368,18 +1253,6 @@ public class PlaybackService extends MediaBrowserServiceCompat { notificationSetupThread.start(); } - private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) { - Intent intent = new Intent( - PlaybackService.this, PlaybackService.class); - intent.putExtra( - MediaButtonReceiver.EXTRA_KEYCODE, - keycodeValue); - return PendingIntent - .getService(PlaybackService.this, requestCode, - intent, - PendingIntent.FLAG_UPDATE_CURRENT); - } - /** * Persists the current position and last played time of the media file. * diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java new file mode 100644 index 000000000..9a1e8e7ef --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java @@ -0,0 +1,184 @@ +package de.danoeh.antennapod.core.service.playback; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.support.annotation.NonNull; +import android.support.v4.app.NotificationCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.util.Log; +import android.view.KeyEvent; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import de.danoeh.antennapod.core.ClientConfig; +import de.danoeh.antennapod.core.R; +import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.receiver.MediaButtonReceiver; +import de.danoeh.antennapod.core.util.IntList; +import de.danoeh.antennapod.core.util.gui.NotificationUtils; +import de.danoeh.antennapod.core.util.playback.Playable; + +public class PlaybackServiceNotificationBuilder extends NotificationCompat.Builder { + private static final String TAG = "PlaybackSrvNotification"; + private static Bitmap defaultIcon = null; + + private Context context; + + public PlaybackServiceNotificationBuilder(@NonNull Context context) { + super(context, NotificationUtils.CHANNEL_ID_PLAYING); + this.context = context; + + final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context); + + final PendingIntent pIntent = PendingIntent.getActivity(context, 0, + PlaybackService.getPlayerActivityIntent(context), + PendingIntent.FLAG_UPDATE_CURRENT); + + setContentTitle(context.getString(R.string.app_name)); + setContentText("Service is running"); // Just in case the notification is not updated (should not occur) + setOngoing(false); + setContentIntent(pIntent); + setWhen(0); // we don't need the time + setSmallIcon(smallIcon); + setPriority(NotificationCompat.PRIORITY_MIN); + } + + public void addMetadata(Playable playable, MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { + Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus); + setContentTitle(playable.getFeedTitle()); + setContentText(playable.getEpisodeTitle()); + setPriority(UserPreferences.getNotifyPriority()); + addActions(mediaSessionToken, playerStatus, isCasting); + setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + setColor(NotificationCompat.COLOR_DEFAULT); + } + + public boolean isIconCached(Playable playable) { + int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); + try { + Bitmap icon = Glide.with(context) + .asBitmap() + .load(playable.getImageLocation()) + .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY)) + .apply(new RequestOptions() + .centerCrop() + .onlyRetrieveFromCache(true)) + .submit(iconSize, iconSize) + .get(); + return icon != null; + } catch (Throwable tr) { + return false; + } + } + + public void loadIcon(Playable playable) { + Bitmap icon = null; + int iconSize = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width); + try { + icon = Glide.with(context) + .asBitmap() + .load(playable.getImageLocation()) + .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY)) + .apply(new RequestOptions().centerCrop()) + .submit(iconSize, iconSize) + .get(); + } catch (Throwable tr) { + Log.e(TAG, "Error loading the media icon for the notification", tr); + } + + if (icon == null) { + loadDefaultIcon(); + } else { + setLargeIcon(icon); + } + } + + public void loadDefaultIcon() { + if (defaultIcon == null) { + defaultIcon = BitmapFactory.decodeResource(context.getResources(), + ClientConfig.playbackServiceCallbacks.getNotificationIconResource(context)); + } + setLargeIcon(defaultIcon); + } + + private void addActions(MediaSessionCompat.Token mediaSessionToken, PlayerStatus playerStatus, boolean isCasting) { + IntList compactActionList = new IntList(); + + int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction + + if (isCasting) { + Intent stopCastingIntent = new Intent(context, PlaybackService.class); + stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true); + PendingIntent stopCastingPendingIntent = PendingIntent.getService(context, + numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT); + addAction(R.drawable.ic_media_cast_disconnect, + context.getString(R.string.cast_disconnect_label), + stopCastingPendingIntent); + numActions++; + } + + // always let them rewind + PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_REWIND, numActions); + addAction(android.R.drawable.ic_media_rew, context.getString(R.string.rewind_label), rewindButtonPendingIntent); + if (UserPreferences.showRewindOnCompactNotification()) { + compactActionList.add(numActions); + } + numActions++; + + if (playerStatus == PlayerStatus.PLAYING) { + PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_PAUSE, numActions); + addAction(android.R.drawable.ic_media_pause, //pause action + context.getString(R.string.pause_label), + pauseButtonPendingIntent); + compactActionList.add(numActions++); + } else { + PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_PLAY, numActions); + addAction(android.R.drawable.ic_media_play, //play action + context.getString(R.string.play_label), + playButtonPendingIntent); + compactActionList.add(numActions++); + } + + // ff follows play, then we have skip (if it's present) + PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions); + addAction(android.R.drawable.ic_media_ff, context.getString(R.string.fast_forward_label), ffButtonPendingIntent); + if (UserPreferences.showFastForwardOnCompactNotification()) { + compactActionList.add(numActions); + } + numActions++; + + if (UserPreferences.isFollowQueue()) { + PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_NEXT, numActions); + addAction(android.R.drawable.ic_media_next, + context.getString(R.string.skip_episode_label), + skipButtonPendingIntent); + if (UserPreferences.showSkipOnCompactNotification()) { + compactActionList.add(numActions); + } + numActions++; + } + + PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction( + KeyEvent.KEYCODE_MEDIA_STOP, numActions); + setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionToken) + .setShowActionsInCompactView(compactActionList.toArray()) + .setShowCancelButton(true) + .setCancelButtonIntent(stopButtonPendingIntent)); + } + + private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) { + Intent intent = new Intent(context, PlaybackService.class); + intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, keycodeValue); + return PendingIntent .getService(context, requestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT); + } +} \ No newline at end of file