From 4abf6b2f5cb7fe38b0f3b4cc13e48c46b0725a2e Mon Sep 17 00:00:00 2001 From: cool-student Date: Tue, 3 Mar 2020 00:35:44 +0100 Subject: [PATCH] Notification Improvements - add MediaStyle notifications for Background and Popup playback - reduce excessive notification updating ( / recreating of Notification.Builder object) when playing background / popup media - add new buffering state indicator (can be disabled) - upscale close icon / downscale replay icon - add notification slot settings - move notification settings to appearance - fix Metadata (song title, artist and album art) sometimes not being set correctly - other misc notification fixes Co-authored-by: wb9688 --- .../newpipe/player/BackgroundPlayer.java | 462 +++--- .../newpipe/player/NotificationUtil.java | 1379 +++++++++++++++++ .../newpipe/player/PopupVideoPlayer.java | 333 ++-- .../player/helper/MediaSessionManager.java | 97 +- .../settings/AppearanceSettingsFragment.java | 15 + .../res/drawable-hdpi/ic_close_white_32dp.png | Bin 0 -> 257 bytes .../drawable-hdpi/ic_replay_white_32dp.png | Bin 0 -> 804 bytes .../res/drawable-mdpi/ic_close_white_32dp.png | Bin 0 -> 542 bytes .../drawable-mdpi/ic_replay_white_32dp.png | Bin 0 -> 583 bytes .../drawable-xhdpi/ic_close_white_32dp.png | Bin 0 -> 666 bytes .../drawable-xhdpi/ic_replay_white_32dp.png | Bin 0 -> 1168 bytes .../drawable-xxhdpi/ic_close_white_32dp.png | Bin 0 -> 436 bytes .../drawable-xxhdpi/ic_replay_white_32dp.png | Bin 0 -> 908 bytes .../drawable-xxxhdpi/ic_close_white_32dp.png | Bin 0 -> 1852 bytes .../drawable-xxxhdpi/ic_replay_white_32dp.png | Bin 0 -> 2580 bytes app/src/main/res/values/settings_keys.xml | 107 ++ app/src/main/res/values/strings.xml | 14 +- app/src/main/res/xml/appearance_settings.xml | 92 +- app/src/main/res/xml/video_audio_settings.xml | 7 - 19 files changed, 2025 insertions(+), 481 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java create mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 943d685b1..efbe2b912 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -19,44 +19,33 @@ package org.schabi.newpipe.player; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import android.view.View; -import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.MediaSourceTag; -import org.schabi.newpipe.util.BitmapUtils; -import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.ThemeHelper; -import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; /** @@ -65,6 +54,9 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; * @author mauriciocolli */ public final class BackgroundPlayer extends Service { + private static final String TAG = "BackgroundPlayer"; + private static final boolean DEBUG = BasePlayer.DEBUG; + public static final String ACTION_CLOSE = "org.schabi.newpipe.player.BackgroundPlayer.CLOSE"; public static final String ACTION_PLAY_PAUSE @@ -79,30 +71,27 @@ public final class BackgroundPlayer extends Service { = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND"; public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD"; + public static final String ACTION_BUFFERING + = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_BUFFERING"; + public static final String ACTION_SHUFFLE + = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_SHUFFLE"; public static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource"; - private static final String TAG = "BackgroundPlayer"; - private static final boolean DEBUG = BasePlayer.DEBUG; - private static final int NOTIFICATION_ID = 123789; - private static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60; + + private BasePlayerImpl basePlayerImpl; + private SharedPreferences sharedPreferences; + + private boolean shouldUpdateOnProgress; // only used for old notifications + private boolean isForwardPressed; + private boolean isRewindPressed; /*////////////////////////////////////////////////////////////////////////// // Service-Activity Binder //////////////////////////////////////////////////////////////////////////*/ - private SharedPreferences sharedPreferences; - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ private PlayerEventListener activityListener; private IBinder mBinder; - private NotificationManager notificationManager; - private NotificationCompat.Builder notBuilder; - private RemoteViews notRemoteView; - private RemoteViews bigNotRemoteView; - private boolean shouldUpdateOnProgress; - private int timesNotificationUpdated; /*////////////////////////////////////////////////////////////////////////// // Service's LifeCycle @@ -113,7 +102,7 @@ public final class BackgroundPlayer extends Service { if (DEBUG) { Log.d(TAG, "onCreate() called"); } - notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); assureCorrectAppLanguage(this); ThemeHelper.setTheme(this); @@ -127,7 +116,7 @@ public final class BackgroundPlayer extends Service { @Override public int onStartCommand(final Intent intent, final int flags, final int startId) { if (DEBUG) { - Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], " + Log.d(TAG, "N_ onStartCommand() called with: intent = [" + intent + "], " + "flags = [" + flags + "], startId = [" + startId + "]"); } basePlayerImpl.handleIntent(intent); @@ -160,7 +149,7 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ private void onClose() { if (DEBUG) { - Log.d(TAG, "onClose() called"); + Log.d(TAG, "N_ onClose() called"); } if (basePlayerImpl != null) { @@ -168,9 +157,8 @@ public final class BackgroundPlayer extends Service { basePlayerImpl.stopActivityBinding(); basePlayerImpl.destroy(); } - if (notificationManager != null) { - notificationManager.cancel(NOTIFICATION_ID); - } + NotificationUtil.getInstance() + .cancelNotification(NotificationUtil.NOTIFICATION_ID_BACKGROUND); mBinder = null; basePlayerImpl = null; @@ -191,168 +179,9 @@ public final class BackgroundPlayer extends Service { } } - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ - - private void resetNotification() { - notBuilder = createNotification(); - timesNotificationUpdated = 0; - } - - private NotificationCompat.Builder createNotification() { - notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_background_notification); - bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_background_notification_expanded); - - setupNotification(notRemoteView); - setupNotification(bigNotRemoteView); - - NotificationCompat.Builder builder = new NotificationCompat - .Builder(this, getString(R.string.notification_channel_id)) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setCustomContentView(notRemoteView) - .setCustomBigContentView(bigNotRemoteView); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - setLockScreenThumbnail(builder); - } - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - builder.setPriority(NotificationCompat.PRIORITY_MAX); - } - return builder; - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private void setLockScreenThumbnail(final NotificationCompat.Builder builder) { - boolean isLockScreenThumbnailEnabled = sharedPreferences.getBoolean( - getString(R.string.enable_lock_screen_video_thumbnail_key), true); - - if (isLockScreenThumbnailEnabled) { - basePlayerImpl.mediaSessionManager.setLockScreenArt( - builder, - getCenteredThumbnailBitmap() - ); - } else { - basePlayerImpl.mediaSessionManager.clearLockScreenArt(builder); - } - } - - @Nullable - private Bitmap getCenteredThumbnailBitmap() { - final int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; - final int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels; - - return BitmapUtils.centerCrop(basePlayerImpl.getThumbnail(), screenWidth, screenHeight); - } - - private void setupNotification(final RemoteViews remoteViews) { - if (basePlayerImpl == null) { - return; - } - - remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); - remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); - - remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationStop, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT)); - - // Starts background player activity -- attempts to unlock lockscreen - final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(this); - remoteViews.setOnClickPendingIntent(R.id.notificationContent, - PendingIntent.getActivity(this, NOTIFICATION_ID, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); - - if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { - remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_previous); - remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_next); - remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT)); - } else { - remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_rewind); - remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_fastforward); - remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT)); - } - - setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode()); - } - - /** - * Updates the notification, and the play/pause button in it. - * Used for changes on the remoteView - * - * @param drawableId if != -1, sets the drawable with that id on the play/pause button - */ - private synchronized void updateNotification(final int drawableId) { -// if (DEBUG) { -// Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); -// } - if (notBuilder == null) { - return; - } - if (drawableId != -1) { - if (notRemoteView != null) { - notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - } - notificationManager.notify(NOTIFICATION_ID, notBuilder.build()); - timesNotificationUpdated++; - } - - /*////////////////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////////////////*/ - - private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { - switch (repeatMode) { - case Player.REPEAT_MODE_OFF: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_off); - break; - case Player.REPEAT_MODE_ONE: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_one); - break; - case Player.REPEAT_MODE_ALL: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_all); - break; - } - } - ////////////////////////////////////////////////////////////////////////// - protected class BasePlayerImpl extends BasePlayer { @NonNull private final AudioPlaybackResolver resolver; - private int cachedDuration; - private String cachedDurationString; BasePlayerImpl(final Context context) { super(context); @@ -367,51 +196,49 @@ public final class BackgroundPlayer extends Service { @Override public void handleIntent(final Intent intent) { super.handleIntent(intent); - - resetNotification(); - if (bigNotRemoteView != null) { - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false); + if (DEBUG) { + Log.d(TAG, "N_ handleIntent()"); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false); - } - startForeground(NOTIFICATION_ID, notBuilder.build()); + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); // false + NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 0, false); + startForeground(NotificationUtil.NOTIFICATION_ID_BACKGROUND, + NotificationUtil.getInstance().notificationBuilder.build()); } /*////////////////////////////////////////////////////////////////////////// // Thumbnail Loading //////////////////////////////////////////////////////////////////////////*/ - private void updateNotificationThumbnail() { - if (basePlayerImpl == null) { - return; - } - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, - basePlayerImpl.getThumbnail()); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, - basePlayerImpl.getThumbnail()); - } - } - @Override public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onLoadingComplete()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); //true + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onLoadingFailed(final String imageUri, final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onLoadingFailed()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); //true + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } /*////////////////////////////////////////////////////////////////////////// @@ -426,6 +253,14 @@ public final class BackgroundPlayer extends Service { @Override public void onShuffleClicked() { super.onShuffleClicked(); + if (DEBUG) { + Log.d(TAG, "N_ onShuffleClicked:"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updatePlayback(); } @@ -440,31 +275,35 @@ public final class BackgroundPlayer extends Service { final int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); - if (!shouldUpdateOnProgress) { - return; - } - if (timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET) { - resetNotification(); + // setMetadata only updates the metadata when any of the metadata keys are null + basePlayerImpl.mediaSessionManager.setMetadata(basePlayerImpl.getVideoTitle(), + basePlayerImpl.getUploaderName(), basePlayerImpl.getThumbnail(), duration); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /*Oreo*/) { - updateNotificationThumbnail(); + boolean areOldNotificationsEnabled = sharedPreferences.getBoolean( + getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + if (!shouldUpdateOnProgress) { + return; } - } - if (bigNotRemoteView != null) { - if (cachedDuration != duration) { - cachedDuration = duration; - cachedDurationString = getTimeString(duration); + if (NotificationUtil.timesNotificationUpdated + > NotificationUtil.NOTIFICATION_UPDATES_BEFORE_RESET) { + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationUtil.getInstance() + .updateOldNotificationsThumbnail(basePlayerImpl); + } } - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, + + NotificationUtil.getInstance().setCachedDuration(currentProgress, duration); + NotificationUtil.getInstance().setProgressbarOnOldNotifications(duration, currentProgress, false); - bigNotRemoteView.setTextViewText(R.id.notificationTime, - getTimeString(currentProgress) + " / " + cachedDurationString); + + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, duration, - currentProgress, false); - } - updateNotification(-1); } @Override @@ -482,12 +321,7 @@ public final class BackgroundPlayer extends Service { @Override public void destroy() { super.destroy(); - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } + NotificationUtil.getInstance().unsetImageInOldNotifications(); } /*////////////////////////////////////////////////////////////////////////// @@ -507,8 +341,14 @@ public final class BackgroundPlayer extends Service { @Override public void onRepeatModeChanged(final int i) { - resetNotification(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onRepeatModeChanged()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updatePlayback(); } @@ -518,9 +358,15 @@ public final class BackgroundPlayer extends Service { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onMetadataChanged()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updateMetadata(); } @@ -585,31 +431,36 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ @Override - protected void setupBroadcastReceiver(final IntentFilter intentFltr) { - super.setupBroadcastReceiver(intentFltr); - intentFltr.addAction(ACTION_CLOSE); - intentFltr.addAction(ACTION_PLAY_PAUSE); - intentFltr.addAction(ACTION_REPEAT); - intentFltr.addAction(ACTION_PLAY_PREVIOUS); - intentFltr.addAction(ACTION_PLAY_NEXT); - intentFltr.addAction(ACTION_FAST_REWIND); - intentFltr.addAction(ACTION_FAST_FORWARD); + protected void setupBroadcastReceiver(final IntentFilter intentFilter) { + super.setupBroadcastReceiver(intentFilter); + intentFilter.addAction(ACTION_CLOSE); + intentFilter.addAction(ACTION_PLAY_PAUSE); + intentFilter.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_PLAY_PREVIOUS); + intentFilter.addAction(ACTION_PLAY_NEXT); + intentFilter.addAction(ACTION_FAST_REWIND); + intentFilter.addAction(ACTION_FAST_FORWARD); + intentFilter.addAction(ACTION_BUFFERING); + intentFilter.addAction(ACTION_SHUFFLE); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); - intentFltr.addAction(Intent.ACTION_SCREEN_ON); - intentFltr.addAction(Intent.ACTION_SCREEN_OFF); - - intentFltr.addAction(Intent.ACTION_HEADSET_PLUG); + intentFilter.addAction(Intent.ACTION_HEADSET_PLUG); } @Override public void onBroadcastReceived(final Intent intent) { super.onBroadcastReceived(intent); + if (intent == null || intent.getAction() == null) { return; } + if (DEBUG) { - Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]"); + // FIXME remove N_ + Log.d(TAG, "N_ onBroadcastReceived() called with: intent = [" + intent + "]"); } + switch (intent.getAction()) { case ACTION_CLOSE: onClose(); @@ -627,9 +478,11 @@ public final class BackgroundPlayer extends Service { onPlayPrevious(); break; case ACTION_FAST_FORWARD: + isForwardPressed = true; onFastForward(); break; case ACTION_FAST_REWIND: + isRewindPressed = true; onFastRewind(); break; case Intent.ACTION_SCREEN_ON: @@ -638,6 +491,17 @@ public final class BackgroundPlayer extends Service { case Intent.ACTION_SCREEN_OFF: onScreenOnOff(false); break; + case ACTION_BUFFERING: + onBuffering(); + break; + case ACTION_SHUFFLE: + onShuffleClicked(); + break; + case "android.intent.action.HEADSET_PLUG": //FIXME + /*notificationManager.cancel(NOTIFICATION_ID); + mediaSessionManager.dispose(); + mediaSessionManager.enable(getBaseContext(), basePlayerImpl.simpleExoPlayer);*/ + break; } } @@ -645,6 +509,31 @@ public final class BackgroundPlayer extends Service { // States //////////////////////////////////////////////////////////////////////////*/ + @Override + public void onBuffering() { + super.onBuffering(); + if (NotificationUtil.getInstance().notificationSlot0.contains("buffering") + || NotificationUtil.getInstance().notificationSlot1.contains("buffering") + || NotificationUtil.getInstance().notificationSlot2.contains("buffering") + || NotificationUtil.getInstance().notificationSlot3.contains("buffering") + || NotificationUtil.getInstance().notificationSlot4.contains("buffering")) { + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + if (!(isForwardPressed || isRewindPressed)) { + if (DEBUG) { + Log.d(TAG, "N_ onBuffering()"); + } + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); + } else { + isForwardPressed = false; + isRewindPressed = false; + } + } + } + } + @Override public void changeState(final int state) { super.changeState(state); @@ -654,31 +543,50 @@ public final class BackgroundPlayer extends Service { @Override public void onPlaying() { super.onPlaying(); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(R.drawable.exo_controls_pause); + + if (DEBUG) { + Log.d(TAG, "N_ onPlaying()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_pause_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onPaused() { super.onPaused(); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(R.drawable.exo_controls_play); + + if (DEBUG) { + Log.d(TAG, "N_ onPaused()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onCompleted() { super.onCompleted(); - resetNotification(); - if (bigNotRemoteView != null) { - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false); + if (DEBUG) { + Log.d(TAG, "N_ onCompleted()"); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false); - } - updateNotificationThumbnail(); - updateNotification(R.drawable.ic_replay_white_24dp); + + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 100, false); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_replay_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java new file mode 100644 index 000000000..68648a8ad --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -0,0 +1,1379 @@ +package org.schabi.newpipe.player; + +import android.annotation.SuppressLint; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.os.Build; +import android.support.v4.media.session.MediaSessionCompat; +import android.util.Log; +import android.widget.RemoteViews; + +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import com.google.android.exoplayer2.Player; + +import org.schabi.newpipe.BuildConfig; +import org.schabi.newpipe.R; +import org.schabi.newpipe.util.NavigationHelper; + +import static android.content.Context.NOTIFICATION_SERVICE; +import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; + +/** + * This is a utility class for player notifications. + * + * @author cool-student + */ +public final class NotificationUtil { + private static final String TAG = "NotificationUtil"; + private static final boolean DEBUG = BasePlayer.DEBUG; + + public static final int NOTIFICATION_ID_BACKGROUND = 123789; + public static final int NOTIFICATION_ID_POPUP = 40028922; + static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60; // only used for old notifications + + static int timesNotificationUpdated; // only used for old notifications + + NotificationCompat.Builder notificationBuilder; + + String notificationSlot0 = "smart_rewind_prev"; + String notificationSlot1 = "play_pause_buffering"; + String notificationSlot2 = "smart_forward_next"; + String notificationSlot3 = "repeat"; + String notificationSlot4 = "close"; + + private NotificationManager notificationManager; + /*private NotificationManager notificationManager; + private NotificationCompat.Builder notificationBuilder;*/ + + private RemoteViews notificationPopupRemoteView; + private RemoteViews notificationRemoteView; // always null when new notifications are used + private RemoteViews bigNotificationRemoteView; // always null when new notifications are used + + private int cachedDuration; // only used for old notifications + private String cachedDurationString; // only used for old notifications + + private NotificationUtil() { } + + public static NotificationUtil getInstance() { + return LazyHolder.INSTANCE; + } + + void recreatePopupPlayerNotification(final Context context, + final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences, + final boolean recreate) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); + } else if (notificationBuilder == null || recreate) { + Log.d(TAG, "N_ recreatePopupPlayerNotification(true)"); + notificationBuilder = createPopupPlayerNotification(context, mediaSessionCompatToken, + playerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreatePopupPlayerNotification(final Context context, + final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); + } else if (notificationBuilder == null) { + Log.d(TAG, "N_ recreatePopupPlayerNotification()"); + notificationBuilder = createPopupPlayerNotification(context, + mediaSessionCompatToken, playerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreateBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences, final boolean recreate) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (notificationBuilder == null || recreate || areOldNotificationsEnabled) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification(true)"); + notificationBuilder = createBackgroundPlayerNotification(context, + mediaSessionCompatToken, basePlayerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreateBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (notificationBuilder == null || areOldNotificationsEnabled) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification()"); + notificationBuilder = createBackgroundPlayerNotification(context, + mediaSessionCompatToken, basePlayerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + NotificationCompat.Builder createBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || areOldNotificationsEnabled) { + notificationRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_background_notification); + bigNotificationRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_background_notification_expanded); + + setupOldNotification(notificationRemoteView, context, basePlayerImpl); + setupOldNotification(bigNotificationRemoteView, context, basePlayerImpl); + + builder + .setOngoing(true) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setCustomContentView(notificationRemoteView) + .setCustomBigContentView(bigNotificationRemoteView) + .setPriority(NotificationCompat.PRIORITY_MAX); + } else { + String compactView = sharedPreferences.getString(context.getString( + R.string.settings_notifications_compact_view_key), "0,1,2"); + int compactSlot0; + int compactSlot1; + int compactSlot2; + try { + String[] parts = compactView.split(","); + compactSlot0 = Integer.parseInt(parts[0]); + compactSlot1 = Integer.parseInt(parts[1]); + compactSlot2 = Integer.parseInt(parts[2]); + if (compactSlot0 > 4) { + compactSlot0 = 0; + } + if (compactSlot1 > 4) { + compactSlot1 = 1; + } + if (compactSlot2 > 4) { + compactSlot2 = 2; + } + } catch (Exception e) { + e.printStackTrace(); + compactSlot0 = 0; + compactSlot1 = 1; + compactSlot2 = 2; + } + + builder + .setStyle(new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionCompatToken) + .setShowCancelButton(false) + .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) + .setOngoing(false) + .setContentIntent(PendingIntent.getActivity(context, NOTIFICATION_ID_BACKGROUND, + new Intent(NavigationHelper.getBackgroundPlayerActivityIntent(context)), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(basePlayerImpl.getVideoTitle()) + .setContentText(basePlayerImpl.getUploaderName()) + .setDeleteIntent(PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setColor(ContextCompat.getColor(context, R.color.gray)) + .setPriority(NotificationCompat.PRIORITY_HIGH); + final boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context + .getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(basePlayerImpl.getThumbnail())); + } else { + builder.setLargeIcon(basePlayerImpl.getThumbnail()); + } + + notificationSlot0 = sharedPreferences.getString( + context.getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = sharedPreferences.getString( + context.getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = sharedPreferences.getString( + context.getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = sharedPreferences.getString( + context.getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = sharedPreferences.getString( + context.getString(R.string.notification_slot_4_key), notificationSlot4); + + addAction(context, builder, basePlayerImpl, notificationSlot0); + addAction(context, builder, basePlayerImpl, notificationSlot1); + addAction(context, builder, basePlayerImpl, notificationSlot2); + addAction(context, builder, basePlayerImpl, notificationSlot3); + addAction(context, builder, basePlayerImpl, notificationSlot4); + } + + return builder; + } + + private void addAction(final Context context, final NotificationCompat.Builder builder, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final String slot) { + switch (slot) { + case "play_pause_buffering": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.ic_file_download_white_24dp, "Buffering", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } else { + builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (basePlayerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + } + break; + case "play_pause": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else if (basePlayerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "rewind": + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_rewind_prev": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "forward": + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_forward_next": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "next": + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "repeat": + switch (basePlayerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + builder.addAction(R.drawable.exo_controls_repeat_one, "RepeatOne", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_ALL: + builder.addAction(R.drawable.exo_controls_repeat_all, "RepeatAll", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_OFF: + default: + builder.addAction(R.drawable.exo_controls_repeat_off, "RepeatOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + break; + case "shuffle": + if (basePlayerImpl.playQueue.isShuffled()) { + builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_shuffle_off, "ShuffleOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "close": + builder.addAction(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + } + + /** + * Updates the notification, and the button icons depending on the playback state. + * On old notifications used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + * @param context + * @param basePlayerImpl + * @param sharedPreferences + */ + synchronized void updateBackgroundPlayerNotification( + final int drawableId, final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + if (DEBUG) { + Log.d(TAG, "N_ updateBackgroundPlayerNotification()"); + } + + if (notificationBuilder == null) { + return; + } + if (drawableId != -1) { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView + .setImageViewResource(R.id.notificationPlayPause, drawableId); + } + } + + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (!areOldNotificationsEnabled) { + notificationBuilder.setContentTitle(basePlayerImpl.getVideoTitle()); + notificationBuilder.setContentText(basePlayerImpl.getUploaderName()); + final boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean( + context.getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + notificationBuilder.setLargeIcon(getBitmapWithSquareAspectRatio(basePlayerImpl + .getThumbnail())); + } else { + notificationBuilder.setLargeIcon(basePlayerImpl.getThumbnail()); + } + + setAction(context, basePlayerImpl, notificationSlot0, 0); + setAction(context, basePlayerImpl, notificationSlot1, 1); + setAction(context, basePlayerImpl, notificationSlot2, 2); + setAction(context, basePlayerImpl, notificationSlot3, 3); + setAction(context, basePlayerImpl, notificationSlot4, 4); + } + + notificationManager.notify(NOTIFICATION_ID_BACKGROUND, notificationBuilder.build()); + + if (areOldNotificationsEnabled) { + timesNotificationUpdated++; + } + } + + @SuppressLint("RestrictedApi") + private void setAction(final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final String slot, final int slotNumber) { + switch (slot) { + case "play_pause_buffering": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_file_download_white_24dp, + "Buffering", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT))); + notificationBuilder.setSmallIcon(android.R.drawable.stat_sys_download); + } else if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (basePlayerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "play_pause": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + if (basePlayerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "rewind": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_rewind_prev": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, + "Prev", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "forward": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_forward_next": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "next": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "repeat": + switch (basePlayerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_one, + "RepeatOne", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_ALL: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_all, + "RepeatAll", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_OFF: + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_off, + "RepeatOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + break; + case "shuffle": + if (basePlayerImpl.playQueue.isShuffled()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, + "ShuffleOn", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_off, + "ShuffleOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "close": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + } + + private NotificationCompat.Builder createPopupPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + String compactView = sharedPreferences.getString(context + .getString(R.string.settings_notifications_compact_view_key), "0,1,2"); + int compactSlot0; + int compactSlot1; + int compactSlot2; + try { + String[] parts = compactView.split(","); + compactSlot0 = Integer.parseInt(parts[0]); + compactSlot1 = Integer.parseInt(parts[1]); + compactSlot2 = Integer.parseInt(parts[2]); + if (compactSlot0 > 4) { + compactSlot0 = 0; + } + if (compactSlot1 > 4) { + compactSlot1 = 1; + } + if (compactSlot2 > 4) { + compactSlot2 = 2; + } + } catch (Exception e) { + e.printStackTrace(); + compactSlot0 = 0; + compactSlot1 = 1; + compactSlot2 = 2; + } + + builder + .setStyle( + new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionCompatToken) + .setShowCancelButton(false) + .setShowActionsInCompactView(compactSlot0, compactSlot1, + compactSlot2)) + .setOngoing(false) + .setContentIntent(PendingIntent.getActivity(context, NOTIFICATION_ID_POPUP, + new Intent(NavigationHelper.getPopupPlayerActivityIntent(context)), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(playerImpl.getVideoTitle()) + .setContentText(playerImpl.getUploaderName()) + .setDeleteIntent(PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setColor(ContextCompat.getColor(context, R.color.gray)); + boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context + .getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(playerImpl.getThumbnail())); + } else { + builder.setLargeIcon(playerImpl.getThumbnail()); + } + notificationSlot0 = sharedPreferences.getString(context + .getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = sharedPreferences.getString(context + .getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = sharedPreferences.getString(context. + getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = sharedPreferences.getString(context + .getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = sharedPreferences.getString(context + .getString(R.string.notification_slot_4_key), notificationSlot4); + + addAction(context, builder, playerImpl, notificationSlot0); + addAction(context, builder, playerImpl, notificationSlot1); + addAction(context, builder, playerImpl, notificationSlot2); + addAction(context, builder, playerImpl, notificationSlot3); + addAction(context, builder, playerImpl, notificationSlot4); + + builder.setPriority(NotificationCompat.PRIORITY_HIGH); + return builder; + } + + private void addAction(final Context context, final NotificationCompat.Builder builder, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, final String slot) { + switch (slot) { + case "play_pause_buffering": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.ic_file_download_white_24dp, "Buffering", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } else { + builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (playerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + } + break; + case "play_pause": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else if (playerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "rewind": + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_rewind_prev": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "forward": + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_forward_next": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "next": + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "repeat": + switch (playerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + builder.addAction(R.drawable.exo_controls_repeat_one, "RepeatOne", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_ALL: + builder.addAction(R.drawable.exo_controls_repeat_all, "RepeatAll", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_OFF: + default: + builder.addAction(R.drawable.exo_controls_repeat_off, "RepeatOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + break; + case "shuffle": + if (playerImpl.playQueue.isShuffled()) { + builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_shuffle_off, "ShuffleOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "close": + builder.addAction(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + } + + /** + * Updates the notification, and the button icons depending on the playback state. + * On old notifications used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + * @param context + * @param playerImpl + * @param sharedPreferences + */ + @SuppressLint("RestrictedApi") + synchronized void updatePopupPlayerNotification( + final int drawableId, final Context context, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + if (DEBUG) { + Log.d(TAG, "N_ updatePopupPlayerNotification()"); + } + + if (notificationBuilder == null) { + return; + } + + boolean areOldNotificationsEnabled = sharedPreferences.getBoolean(context + .getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + updateOldPopupPlayerNotification(drawableId); + } else { + notificationBuilder.setContentTitle(playerImpl.getVideoTitle()); + notificationBuilder.setContentText(playerImpl.getUploaderName()); + boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context. + getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + notificationBuilder + .setLargeIcon(getBitmapWithSquareAspectRatio(playerImpl.getThumbnail())); + } else { + notificationBuilder.setLargeIcon(playerImpl.getThumbnail()); + } + + setAction(context, playerImpl, notificationSlot0, 0); + setAction(context, playerImpl, notificationSlot1, 1); + setAction(context, playerImpl, notificationSlot2, 2); + setAction(context, playerImpl, notificationSlot3, 3); + setAction(context, playerImpl, notificationSlot4, 4); + } + + notificationManager.notify(NOTIFICATION_ID_POPUP, notificationBuilder.build()); + + if (areOldNotificationsEnabled) { + timesNotificationUpdated++; + } + } + + @SuppressLint("RestrictedApi") + private void setAction(final Context context, final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final String slot, final int slotNumber) { + switch (slot) { + case "play_pause_buffering": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_file_download_white_24dp, + "Buffering", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT))); + notificationBuilder.setSmallIcon(android.R.drawable.stat_sys_download); + } else if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (playerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "play_pause": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + if (playerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "rewind": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, + "Rewind", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_rewind_prev": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, + "Prev", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, + "Rewind", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "forward": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_forward_next": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "next": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "repeat": + switch (playerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_one, + "RepeatOne", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_ALL: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_all, + "RepeatAll", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_OFF: + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_off, + "RepeatOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + break; + case "shuffle": + if (playerImpl.playQueue.isShuffled()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, + "ShuffleOn", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_off, + "ShuffleOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "close": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "n/a": // do nothing + /*try { //FIXME maybe do nothing here ? + notificationBuilder.mActions.remove(slotNumber); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + }*/ + break; + case "prev": + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + } + + private Bitmap getBitmapWithSquareAspectRatio(final Bitmap bitmap) { + return getResizedBitmap(bitmap, bitmap.getWidth(), bitmap.getWidth()); + } + + private Bitmap getResizedBitmap(final Bitmap bitmap, final int newWidth, final int newHeight) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false); + } + + @Deprecated // only used for old notifications + private void setupOldNotification(final RemoteViews remoteViews, final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl) { + if (basePlayerImpl == null) { + return; + } + + remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); + remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); + + remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationStop, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + + // Starts background player activity -- attempts to unlock lockscreen + final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(context); + remoteViews.setOnClickPendingIntent(R.id.notificationContent, + PendingIntent.getActivity(context, NOTIFICATION_ID_BACKGROUND, intent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + remoteViews.setInt(R.id.notificationFRewind, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous); + remoteViews.setInt(R.id.notificationFForward, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next); + remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationFForward, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + remoteViews.setInt(R.id.notificationFRewind, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_rewind); + remoteViews.setInt(R.id.notificationFForward, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_fastforward); + remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationFForward, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + + setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode()); + } + + @Deprecated // only used for old notifications + private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { + switch (repeatMode) { + case Player.REPEAT_MODE_OFF: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_off); + break; + case Player.REPEAT_MODE_ONE: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_one); + break; + case Player.REPEAT_MODE_ALL: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_all); + break; + } + } + + @Deprecated // only used for old notifications + public void updateOldNotificationsThumbnail( + final BackgroundPlayer.BasePlayerImpl basePlayerImpl) { + if (basePlayerImpl == null) { + return; + } + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, + basePlayerImpl.getThumbnail()); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView.setImageViewBitmap(R.id.notificationCover, + basePlayerImpl.getThumbnail()); + } + } + + @Deprecated // only used for old notifications + public void setProgressbarOnOldNotifications(final int max, final int progress, + final boolean indeterminate) { + if (bigNotificationRemoteView != null) { //FIXME put in Util and turn into a method + bigNotificationRemoteView.setProgressBar(R.id.notificationProgressBar, max, progress, + indeterminate); + } + if (notificationRemoteView != null) { + notificationRemoteView.setProgressBar(R.id.notificationProgressBar, max, progress, + indeterminate); + } + } + + @Deprecated // only used for old notifications + public void unsetImageInOldNotifications() { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + } + + @Deprecated // only used for old notifications + public void setCachedDuration(final int currentProgress, final int duration) { + if (bigNotificationRemoteView != null) { + if (cachedDuration != duration) { + cachedDuration = duration; + cachedDurationString = getTimeString(duration); + } + bigNotificationRemoteView.setTextViewText(R.id.notificationTime, + getTimeString(currentProgress) + " / " + cachedDurationString); + } + } + + @Deprecated + private NotificationCompat.Builder createOldPopupPlayerNotification( + final Context context, final PopupVideoPlayer.VideoPlayerImpl playerImpl) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + notificationBuilder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + notificationPopupRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_popup_notification); + + notificationPopupRemoteView.setTextViewText(R.id.notificationSongName, + playerImpl.getVideoTitle()); + notificationPopupRemoteView.setTextViewText(R.id.notificationArtist, + playerImpl.getUploaderName()); + notificationPopupRemoteView.setImageViewBitmap(R.id.notificationCover, + playerImpl.getThumbnail()); + + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationStop, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationRepeat, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + + // Starts popup player activity -- attempts to unlock lockscreen + final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(context); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationContent, + PendingIntent.getActivity(context, NOTIFICATION_ID_POPUP, intent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + setRepeatPopupModeRemote(notificationPopupRemoteView, playerImpl.getRepeatMode()); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)) + .setOngoing(true) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContent(notificationPopupRemoteView); + + builder.setPriority(NotificationCompat.PRIORITY_MAX); + return builder; + } + + /* + * Updates the notification, and the play/pause button in it. + * Used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + */ + @Deprecated + private void updateOldPopupPlayerNotification(final int drawableId) { + if (DEBUG) { + Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); + } + if (notificationBuilder == null || notificationPopupRemoteView == null) { + return; + } + if (drawableId != -1) { + notificationPopupRemoteView.setImageViewResource(R.id.notificationPlayPause, + drawableId); + } + notificationManager.notify(NOTIFICATION_ID_POPUP, notificationBuilder.build()); + } + + @Deprecated // only used for old notifications + protected void setRepeatPopupModeRemote(final RemoteViews remoteViews, final int repeatMode) { + final String methodName = "setImageResource"; + + if (remoteViews == null) { + return; + } + + switch (repeatMode) { + case Player.REPEAT_MODE_OFF: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_off); + break; + case Player.REPEAT_MODE_ONE: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_one); + break; + case Player.REPEAT_MODE_ALL: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_all); + break; + } + } + + @Deprecated // only used for old notifications + public void unsetImageInOldPopupNotifications() { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + } + + public void cancelNotification(final int id) { + try { + if (notificationManager != null) { + notificationManager.cancel(id); + } + } catch (Exception e) { + Log.e("NotificationUtil", "Exception", e); + } + } + + private static class LazyHolder { + private static final NotificationUtil INSTANCE = new NotificationUtil(); + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 0ccec3067..a1ad139f6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -22,8 +22,6 @@ package org.schabi.newpipe.player; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -48,23 +46,19 @@ import android.view.animation.AnticipateInterpolator; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.PopupMenu; -import android.widget.RemoteViews; import android.widget.SeekBar; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.core.app.NotificationCompat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.text.CaptionStyleCompat; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.player.event.PlayerEventListener; @@ -89,13 +83,28 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; * @author mauriciocolli */ public final class PopupVideoPlayer extends Service { - public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE"; - public static final String ACTION_PLAY_PAUSE - = "org.schabi.newpipe.player.PopupVideoPlayer.PLAY_PAUSE"; - public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT"; private static final String TAG = ".PopupVideoPlayer"; private static final boolean DEBUG = BasePlayer.DEBUG; - private static final int NOTIFICATION_ID = 40028922; + + public static final String ACTION_CLOSE + = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE"; + public static final String ACTION_PLAY_PAUSE + = "org.schabi.newpipe.player.PopupVideoPlayer.PLAY_PAUSE"; + public static final String ACTION_REPEAT + = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT"; + public static final String ACTION_FAST_REWIND + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_FAST_REWIND"; + public static final String ACTION_FAST_FORWARD + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_FAST_FORWARD"; + public static final String ACTION_PLAY_NEXT + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_PLAY_NEXT"; + public static final String ACTION_PLAY_PREVIOUS + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_PLAY_PREVIOUS"; + public static final String ACTION_BUFFERING + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_BUFFERING"; + public static final String ACTION_SHUFFLE + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_SHUFFLE"; + private static final String POPUP_SAVED_WIDTH = "popup_saved_width"; private static final String POPUP_SAVED_X = "popup_saved_x"; private static final String POPUP_SAVED_Y = "popup_saved_y"; @@ -126,12 +135,12 @@ public final class PopupVideoPlayer extends Service { private float maximumWidth; private float maximumHeight; - private NotificationManager notificationManager; - private NotificationCompat.Builder notBuilder; - private RemoteViews notRemoteView; + private boolean isForwardPressed; + private boolean isRewindPressed; private VideoPlayerImpl playerImpl; private boolean isPopupClosing = false; + private SharedPreferences sharedPreferences; /*////////////////////////////////////////////////////////////////////////// // Service-Activity Binder @@ -148,7 +157,7 @@ public final class PopupVideoPlayer extends Service { public void onCreate() { assureCorrectAppLanguage(this); windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); - notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); playerImpl = new VideoPlayerImpl(this); ThemeHelper.setTheme(this); @@ -220,9 +229,9 @@ public final class PopupVideoPlayer extends Service { final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(this); final float defaultSize = getResources().getDimension(R.dimen.popup_default_width); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); popupWidth = popupRememberSizeAndPos - ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize; + ? sharedPrefs.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize; final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE @@ -236,16 +245,16 @@ public final class PopupVideoPlayer extends Service { popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - int centerX = (int) (screenWidth / 2f - popupWidth / 2f); - int centerY = (int) (screenHeight / 2f - popupHeight / 2f); + final int centerX = (int) (screenWidth / 2f - popupWidth / 2f); + final int centerY = (int) (screenHeight / 2f - popupHeight / 2f); popupLayoutParams.x = popupRememberSizeAndPos - ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX; + ? sharedPrefs.getInt(POPUP_SAVED_X, centerX) : centerX; popupLayoutParams.y = popupRememberSizeAndPos - ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY; + ? sharedPrefs.getInt(POPUP_SAVED_Y, centerY) : centerY; checkPopupPositionBounds(); - PopupWindowGestureListener listener = new PopupWindowGestureListener(); + final PopupWindowGestureListener listener = new PopupWindowGestureListener(); popupGestureDetector = new GestureDetector(this, listener); rootView.setOnTouchListener(listener); @@ -282,71 +291,6 @@ public final class PopupVideoPlayer extends Service { windowManager.addView(closeOverlayView, closeOverlayLayoutParams); } - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ - - private void resetNotification() { - notBuilder = createNotification(); - } - - private NotificationCompat.Builder createNotification() { - notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_popup_notification); - - notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle()); - notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName()); - notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail()); - - notRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), - PendingIntent.FLAG_UPDATE_CURRENT)); - notRemoteView.setOnClickPendingIntent(R.id.notificationStop, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), - PendingIntent.FLAG_UPDATE_CURRENT)); - notRemoteView.setOnClickPendingIntent(R.id.notificationRepeat, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), - PendingIntent.FLAG_UPDATE_CURRENT)); - - // Starts popup player activity -- attempts to unlock lockscreen - final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(this); - notRemoteView.setOnClickPendingIntent(R.id.notificationContent, - PendingIntent.getActivity(this, NOTIFICATION_ID, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); - - setRepeatModeRemote(notRemoteView, playerImpl.getRepeatMode()); - - NotificationCompat.Builder builder = new NotificationCompat - .Builder(this, getString(R.string.notification_channel_id)) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContent(notRemoteView); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - builder.setPriority(NotificationCompat.PRIORITY_MAX); - } - return builder; - } - - /** - * Updates the notification, and the play/pause button in it. - * Used for changes on the remoteView - * - * @param drawableId if != -1, sets the drawable with that id on the play/pause button - */ - private void updateNotification(final int drawableId) { - if (DEBUG) { - Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); - } - if (notBuilder == null || notRemoteView == null) { - return; - } - if (drawableId != -1) { - notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - notificationManager.notify(NOTIFICATION_ID, notBuilder.build()); - } - /*////////////////////////////////////////////////////////////////////////// // Misc //////////////////////////////////////////////////////////////////////////*/ @@ -372,9 +316,8 @@ public final class PopupVideoPlayer extends Service { } mBinder = null; - if (notificationManager != null) { - notificationManager.cancel(NOTIFICATION_ID); - } + + NotificationUtil.getInstance().cancelNotification(NotificationUtil.NOTIFICATION_ID_POPUP); animateOverlayAndFinishService(); } @@ -461,11 +404,11 @@ public final class PopupVideoPlayer extends Service { } private void savePositionAndSize() { - SharedPreferences sharedPreferences = PreferenceManager + final SharedPreferences sharedPrefs = PreferenceManager .getDefaultSharedPreferences(PopupVideoPlayer.this); - sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply(); - sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply(); - sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply(); + sharedPrefs.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply(); + sharedPrefs.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply(); + sharedPrefs.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply(); } private float getMinimumVideoHeight(final float width) { @@ -530,29 +473,6 @@ public final class PopupVideoPlayer extends Service { windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams); } - protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) { - final String methodName = "setImageResource"; - - if (remoteViews == null) { - return; - } - - switch (repeatMode) { - case Player.REPEAT_MODE_OFF: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_off); - break; - case Player.REPEAT_MODE_ONE: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_one); - break; - case Player.REPEAT_MODE_ALL: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_all); - break; - } - } - private void updateWindowFlags(final int flags) { if (popupLayoutParams == null || windowManager == null || playerImpl == null) { return; @@ -579,8 +499,11 @@ public final class PopupVideoPlayer extends Service { public void handleIntent(final Intent intent) { super.handleIntent(intent); - resetNotification(); - startForeground(NOTIFICATION_ID, notBuilder.build()); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + startForeground(NotificationUtil.NOTIFICATION_ID_POPUP, + NotificationUtil.getInstance().notificationBuilder.build()); } @Override @@ -622,9 +545,7 @@ public final class PopupVideoPlayer extends Service { @Override public void destroy() { - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } + NotificationUtil.getInstance().unsetImageInOldPopupNotifications(); super.destroy(); } @@ -683,6 +604,11 @@ public final class PopupVideoPlayer extends Service { @Override public void onShuffleClicked() { super.onShuffleClicked(); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); updatePlayback(); } @@ -697,6 +623,10 @@ public final class PopupVideoPlayer extends Service { final int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); super.onUpdateProgress(currentProgress, duration, bufferPercent); + + // setMetadata only updates the metadata when any of the metadata keys are null + playerImpl.mediaSessionManager.setMetadata(playerImpl.getVideoTitle(), + playerImpl.getUploaderName(), playerImpl.getThumbnail(), duration); } @Override @@ -724,28 +654,38 @@ public final class PopupVideoPlayer extends Service { public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); + if (playerImpl == null) { return; } - // rebuild notification here since remote view does not release bitmaps, + // rebuild (old) notification here since remote view does not release bitmaps, // causing memory leaks - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override public void onLoadingFailed(final String imageUri, final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override public void onLoadingCancelled(final String imageUri, final View view) { super.onLoadingCancelled(imageUri, view); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } /*////////////////////////////////////////////////////////////////////////// @@ -799,10 +739,12 @@ public final class PopupVideoPlayer extends Service { @Override public void onRepeatModeChanged(final int i) { super.onRepeatModeChanged(i); - setRepeatModeRemote(notRemoteView, i); updatePlayback(); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override @@ -817,8 +759,11 @@ public final class PopupVideoPlayer extends Service { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); updateMetadata(); } @@ -833,18 +778,25 @@ public final class PopupVideoPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ @Override - protected void setupBroadcastReceiver(final IntentFilter intentFltr) { - super.setupBroadcastReceiver(intentFltr); + + protected void setupBroadcastReceiver(final IntentFilter intentFilter) { + super.setupBroadcastReceiver(intentFilter); if (DEBUG) { Log.d(TAG, "setupBroadcastReceiver() called with: " - + "intentFilter = [" + intentFltr + "]"); + + "intentFilter = [" + intentFilter + "]"); } - intentFltr.addAction(ACTION_CLOSE); - intentFltr.addAction(ACTION_PLAY_PAUSE); - intentFltr.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_CLOSE); + intentFilter.addAction(ACTION_PLAY_PAUSE); + intentFilter.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_PLAY_PREVIOUS); + intentFilter.addAction(ACTION_PLAY_NEXT); + intentFilter.addAction(ACTION_FAST_REWIND); + intentFilter.addAction(ACTION_FAST_FORWARD); + intentFilter.addAction(ACTION_BUFFERING); + intentFilter.addAction(ACTION_SHUFFLE); - intentFltr.addAction(Intent.ACTION_SCREEN_ON); - intentFltr.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); } @Override @@ -872,6 +824,26 @@ public final class PopupVideoPlayer extends Service { case Intent.ACTION_SCREEN_OFF: enableVideoRenderer(false); break; + case ACTION_PLAY_NEXT: + onPlayNext(); + break; + case ACTION_PLAY_PREVIOUS: + onPlayPrevious(); + break; + case ACTION_FAST_FORWARD: + isForwardPressed = true; + onFastForward(); + break; + case ACTION_FAST_REWIND: + isRewindPressed = true; + onFastRewind(); + break; + case ACTION_BUFFERING: + onBuffering(); + break; + case ACTION_SHUFFLE: + onShuffleClicked(); + break; } } @@ -888,8 +860,13 @@ public final class PopupVideoPlayer extends Service { @Override public void onBlocked() { super.onBlocked(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); } @Override @@ -898,20 +875,48 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.exo_controls_pause); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_pause_white_24dp, getBaseContext(), + playerImpl, sharedPreferences); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_pause); hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - startForeground(NOTIFICATION_ID, notBuilder.build()); + startForeground(NotificationUtil.NOTIFICATION_ID_POPUP, + NotificationUtil.getInstance().notificationBuilder.build()); } @Override public void onBuffering() { super.onBuffering(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + if (NotificationUtil.getInstance().notificationSlot0.contains("buffering") + || NotificationUtil.getInstance().notificationSlot1.contains("buffering") + || NotificationUtil.getInstance().notificationSlot2.contains("buffering") + || NotificationUtil.getInstance().notificationSlot3.contains("buffering") + || NotificationUtil.getInstance().notificationSlot4.contains("buffering")) { + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + if (!(isForwardPressed || isRewindPressed)) { + if (DEBUG) { + Log.d(TAG, "N_ onBuffering()"); + } + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + } else { + isForwardPressed = false; + isRewindPressed = false; + } + } + } } @Override @@ -920,9 +925,14 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); - videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + + videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white_24dp); stopForeground(false); } @@ -930,8 +940,13 @@ public final class PopupVideoPlayer extends Service { @Override public void onPausedSeek() { super.onPausedSeek(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play); } @@ -942,8 +957,13 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.ic_replay_white_24dp); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_replay_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white_24dp); stopForeground(false); @@ -1004,7 +1024,6 @@ public final class PopupVideoPlayer extends Service { private float initSecPointerX = -1; private float initSecPointerY = -1; - @Override public boolean onDoubleTap(final MotionEvent e) { if (DEBUG) { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java index e101e2185..d0939a914 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java @@ -3,17 +3,14 @@ package org.schabi.newpipe.player.helper; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.media.MediaMetadata; -import android.os.Build; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; +import android.util.Log; import android.view.KeyEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; -import androidx.media.app.NotificationCompat.MediaStyle; import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.Player; @@ -30,13 +27,29 @@ public class MediaSessionManager { private final MediaSessionCompat mediaSession; @NonNull private final MediaSessionConnector sessionConnector; + @NonNull + private final PlaybackStateCompat.Builder playbackStateCompatBuilder; + + private int tmpThumbHash; public MediaSessionManager(@NonNull final Context context, @NonNull final Player player, @NonNull final MediaSessionCallback callback) { this.mediaSession = new MediaSessionCompat(context, TAG); + this.mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS + | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); this.mediaSession.setActive(true); + this.playbackStateCompatBuilder = new PlaybackStateCompat.Builder(); + this.playbackStateCompatBuilder.setState(PlaybackStateCompat.STATE_NONE, -1, 1); + this.playbackStateCompatBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO + | PlaybackStateCompat.ACTION_PLAY + | PlaybackStateCompat.ACTION_PAUSE // was play and pause now play/pause + | PlaybackStateCompat.ACTION_SKIP_TO_NEXT + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_SET_REPEAT_MODE | PlaybackStateCompat.ACTION_STOP); + this.mediaSession.setPlaybackState(playbackStateCompatBuilder.build()); + this.sessionConnector = new MediaSessionConnector(mediaSession); this.sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback)); this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback)); @@ -49,37 +62,65 @@ public class MediaSessionManager { return MediaButtonReceiver.handleIntent(mediaSession, intent); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public void setLockScreenArt(final NotificationCompat.Builder builder, - @Nullable final Bitmap thumbnailBitmap) { - if (thumbnailBitmap == null || !mediaSession.isActive()) { + public MediaSessionCompat.Token getSessionToken() { + return this.mediaSession.getSessionToken(); + } + + public void setMetadata(final String title, final String artist, final Bitmap albumArt, + final long duration) { + if (albumArt == null || !mediaSession.isActive()) { return; } - mediaSession.setMetadata( - new MediaMetadataCompat.Builder() - .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, thumbnailBitmap) - .build() - ); + if (getMetadataAlbumArt() == null) { + Log.d(TAG, "N_getMetadataAlbumArt: thumb == null"); + } + if (getMetadataTitle() == null) { + Log.d(TAG, "N_getMetadataTitle: title == null"); + } + if (getMetadataArtist() == null) { + Log.d(TAG, "N_getMetadataArtist: artist == null"); + } + if (getMetadataDuration() <= 1) { + Log.d(TAG, "N_getMetadataDuration: duration <= 1; " + getMetadataDuration()); + } - MediaStyle mediaStyle = new MediaStyle() - .setMediaSession(mediaSession.getSessionToken()); - - builder.setStyle(mediaStyle); + if (getMetadataAlbumArt() == null || getMetadataTitle() == null + || getMetadataArtist() == null || getMetadataDuration() <= 1 + || albumArt.hashCode() != tmpThumbHash) { + Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist + + " thumb: " + albumArt.hashCode() + " d: " + duration); + mediaSession.setMetadata( + new MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) + .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, albumArt) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration) + .build() + ); + tmpThumbHash = albumArt.hashCode(); + } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public void clearLockScreenArt(final NotificationCompat.Builder builder) { - mediaSession.setMetadata( - new MediaMetadataCompat.Builder() - .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null) - .build() - ); + private Bitmap getMetadataAlbumArt() { + return mediaSession.getController().getMetadata() + .getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART); + } - MediaStyle mediaStyle = new MediaStyle() - .setMediaSession(mediaSession.getSessionToken()); + private String getMetadataTitle() { + return mediaSession.getController().getMetadata() + .getString(MediaMetadataCompat.METADATA_KEY_TITLE); + } - builder.setStyle(mediaStyle); + private String getMetadataArtist() { + return mediaSession.getController().getMetadata() + .getString(MediaMetadataCompat.METADATA_KEY_ARTIST); + } + + private long getMetadataDuration() { + return mediaSession.getController().getMetadata() + .getLong(MediaMetadataCompat.METADATA_KEY_DURATION); } /** diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index a9531693c..c9367c4c9 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -11,6 +11,7 @@ import androidx.annotation.Nullable; import androidx.preference.Preference; import org.schabi.newpipe.R; +import org.schabi.newpipe.player.NotificationUtil; import org.schabi.newpipe.util.Constants; public class AppearanceSettingsFragment extends BasePreferenceFragment { @@ -52,8 +53,22 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment { final Preference captionSettings = findPreference(captionSettingsKey); getPreferenceScreen().removePreference(captionSettings); } + + findPreference(getString(R.string.enable_old_notifications_key)) + .setOnPreferenceChangeListener(oldNotificationsOnPreferenceChangeListener); } + private Preference.OnPreferenceChangeListener oldNotificationsOnPreferenceChangeListener + = (preference, newValue) -> { +// NotificationUtil.getInstance().toast(getContext(), +// "Killed background / popup player notification(s) !"); + NotificationUtil.getInstance() + .cancelNotification(NotificationUtil.NOTIFICATION_ID_BACKGROUND); + NotificationUtil.getInstance().cancelNotification(NotificationUtil.NOTIFICATION_ID_POPUP); + + return true; + }; + @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResource(R.xml.appearance_settings); diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b7c7ffd0e795ba76ed3a062566c9016448795f7a GIT binary patch literal 257 zcmV+c0sj7pP) zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1dd1a6659904ba44561dbfe1b3b3caed950058 GIT binary patch literal 804 zcmV+<1Ka$GP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=Y>Ua!!*MzB~-b zcPe>dSN|c`ndF7t{f0bqlNT=1U&ygAdEsLHg#6|vZ+@fC=nZ;~4$-zlEDw?>lY9~1 z(FxjhxXN6x?V3yIA$m_Ej?iX&wQH!j&RfW$hU_?Ow@~q-My#W2=pK5EKA=3#(4NC~ z3l$eO!bMWJw1)Q4sr&(IM8DpI-810YLNDS%)2KW3XH3|Y3+@O#jSEh5D)|Lordfoj9<&It1B9TGq4d+uaz%JeY(|X>Kb)ARWN7 z=t*taT^q$h9~C&mvEB6{*e?1Nhy91Pz_r&A#Xv6=FoU+!&7RB%sILbhI*e3qgziPT zab2W22)c=M+zZj0ICpo_Jt%zg;vLdwm*yex4${FpL}Nnx`GvES${-Ecu+NK6=!T27 z0~PF{yhaF3vDy;fN7|TeSo*DN^5uPv5Z!%Np(4GByzW#Lb(?mdd(AdV#S~tjs60{lf39H8_eGT0000& literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..20b61eaeec893baf305a701f78fc9c18eee0217d GIT binary patch literal 542 zcmV+(0^$9MP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0kcU&K~z{r?UqYU z0zni-(V?J2*o-c~&pJf+060|H7;Zoqh$HzVO3FzR68}UcLM3 z)Ag!L1qu{M!fZA>fJd4-emjh9!LfZi(y}={aMTvo;Shm{vJab7_dH|;*vs$=KH5hUmqyJ)vH?d4 zHeug_11Q0jZ-PEe_yUeR_5o_bsgEue5`tkTCQy?o+Z-mU;1iE|f(@JV!vPdD1kl#D zY?fVT9#;NLaZWj(;8uWO6>61XC%W(!wqehG0|(E1ZBqeZ?SxjV;2-dnfNzxV@E$g3 z$9|++1A*EK48K8pN^xUzChWTW8(^j3p7)@RU}~zaBS_2>xq$fcJy*bMZ9QezeX)5W z7Z6Om=hPST#0&h9CvpMJklSPLx$1B^Po#P~5`k6)p~c)Dd(YJeHHpQyqeXSIRE%>t z^RWe$+Y|4(Y>d%vg|}k_rIGzH!hiWWPLPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0o+MMK~z{r?Up@D z!%!548!hOdv<0i9j_RP`p!f%L7VYNl>gJ$>n}a{YNjEzRx;O|fej#-eoI6y74t@~d z_vTGWNMq9W8W8%x!%fb)=bUS;iMfA`>$=Oj%pMMhue!|^e>ij%%8uZ0qHs1~UT8Q` z>hHm<$Z)X+nH$ws`)EMB;Xyv(Ijq0|mD&Lf(;OhkHJnws9ndiD5SwrhUche##5-J3 zxgF3jatNgixSfV)CgIV9OG?`T4MT@exCKHEJTyss?9ni3K2!%p043C%>`%2jZ zP0b;68v(fztV68~#%@@`7899Q#cuHrnjcdb-&<(QmJ8$Y6*KOlM)ncm{N}#EGd-;_o~KcU8(e-h*nLx z#|@lRIu`EHUY}E_Y=@w*2p`x*@cF+B$F-#eViSTtVCW^hrT3_XEl~ V_uUT1{e}Pl002ovPDHLkV1lm9{aOG3 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..cc13347341479e29fc567f6af466875e91e4048c GIT binary patch literal 666 zcmV;L0%iS)P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0xwBKK~#8N?V4du z0x=MU(H~w16Yv6_0Zy%_&_u7GzwmozAY|O_E^D{6?0m^fgmydAz6TQ6RS*P05ClOG z1VJ!?!{P9Vp6y{VR1>Z3VRpSaWP{-OhVxDoYYH$L4lXpYtA?o0J4SXJc(S@1e;_ zNzijP)`c=ejLr> zyoWW1l2Go)QHrgRev)sbHb5XocB<}CqX@IY`7d0)h#kQ zglpd-vq5(@Tpo%q^Cqbv2!bF8f*=Tjc3G`{0C;dH`T`V^N&o-=07*qoM6N<$f@YH? A5dZ)H literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa4344735f31f4726c06f74a8282554c0eec7a2 GIT binary patch literal 1168 zcmV;B1aJF^P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1SLsCK~#8N?V8_< z6=fL5_efgpvvr?4ngx z5WHCxL7{9>Q7{wwwK@B+;?YU%&MqDmkEV2J_AtG8Lx*Pv)Z13Pp_lp}YITY?^m2bgo!;UNx%4m8 zm|DCcxBi6sIu>upwf~^D%;F8X_ZQTat9V00YNI7v8q{PzZ;zri(Sd9ZqC4nMbP=6F z=UT1SB(}G(;!&=5K7(86JG2o!q3OehM9V5Z$rZE*J*`lsGDkLKmZC4w1)8uSIua@* zSkw~pZU6#6O} z;O8^Bh6xF#HbE|#;Mzi8M1x!&pcQfr6B6_`K`-Y5cWc7IXrRk+xrGZ!I!(|^xnamW z^jkF8W|DhRl#kUQ#&>@Gp!2cokdv(!K8W|)*g{T+ zv|SDa`vCc^owhnx;El0~W7r7h2MdcyJp4ucCmaPL{>~V6<{Zy&w*Wjq^bk=dv8Payw zAm+=MweemX$7FL+v;n@~**3=~@n%=qjePkRx)&$}UAG$$dqR`sZ`8GQdSVTtI~F*> zc_gojAi%CDl*{*WsT^V*ibFc=luLCGty(ibwV9xh7w~t>@>3{a<2my!CO^fcq*6H`6 zqTezOHV66?*?OOk8^~_dx(|VEbvErIY1kvJRdO#3V^?dAG%WU8a<2yx*oN#K@5C=? zGn%VmshE#^ZA>RD=2i`xF{PY0Br<~e7G+(9?;=|XK8q&MqcuTW99@Q9M{l64TkLyi zJ3o1UjCTWoCagmCf1awjj8-aaSdiF!v>*M4%EspW8qHU@a3RUH=maVYo9j(Y-#d`B zHR2m&_Xn6?(TivvGVGtC*OC2G^fWqw#<32ETvhYyB39D^lU%DgQgv&#U%8l^-CA%qY@2qA=!B5=cm zUgehujQG*l{{`@rPr!f|fEk^>KI9WteE@ilV6HEl&_rJ@p_zU*;T}RirIc{5NocNLm*7JGdV(AM zYYDFOvk5~8{Z*t_>U=!XvoehUSEh=adIga45oe)B9kGgT}7UT3Cirmr(oHPv^YQ1-p=Hlh5u;xggf zX{&yw+El-OrrKQJRl@b7x{HLmNkj95`a#LLnW{Veb2C+!`ppt#r)=g4@?c8RY{yJj~W@W|h^mU4q`i)2y~Rw@J`jIh$1%|In!~{Tb{nMqaxlgb+dq eA%qY@zJ@mriVM?qfwL0;00007!(!0)U7tJ0c*i|=W=`GD{Rhrh*S+VSbLJ}_yb|Y} z2=nyPP+9@g9^_WikSR{kYsy-hy zEku#&*XNV6oCC6@Y0~GT-ej9>Czv`dtH=_DbXwRliC~qaN~fj83SwBz>9x4qN(Rvr ze!Z60m#~`DYlFi_98nBIuPvr=tUC1CrC;Y$3W(^FgzQC`p+k!}pOw6jBq=iPfkh0TWmS)dUz^Bf7(L-mV&-4}J$XOF7sBlc6Scm>hhDc(^Uj{LXd-%JWIiC2AM`rMs#_tFGOcEm> zQ6{J?t)HM6bg;rDU%4Peu#|sCR!~qE~FTC?knj22pV}>5;)E~ds^l}5-05`x5 ia0A=`H^2>0aQz3T@7w=K#~w`p0000Rol24V(Ul{cf{Ft-O^TClm@M})Kx4+>gFppf_Al9 z#ZupGE6zrcR@~QaqeT$ZRuoaI`|%I#4>R+;&olGR%=^sqOcse~F9%hJfcj#!iW);ZTYO z>T=`0X?%9WrcrsC_4A<7*sb<(ZqAXCz$&?zvtCH)DOTz*!{g)CUw2Q0|C}824PDMP z{$6;es_Q`y_y6;2&Fj*OPLzVPYLCjEjk4Z}J3<9+DeG=LyRliiHMy^7kYa3n-Q4^r z=Je=Wh=B)$5psZ;8FFDrR_u@>qb1o1CW4Lrhu0eYPa_i2OYrb(DZ%v))2sppo^;_8 zwb0{z5aLC)COh6l@b#o`w)J1gE$qmZkTWeSW5#}~Xiul~Dxm4=GI?L5oo$KYsh#st zwF=q4KstT`?fKPzz&H~P!{@!{OvE10md89lIPwS=WZaj)>KZoRD~V{@46XE6=K{7Z z#TJUPzMa;tA4a)CC9urOT$T|k$R(Y8;K}A59pxC00f8W^h%BO>TYr4?7hHASPMEOW+xq zFH~-%I>LxXKFc)6rpyej6+LE67n2X3+)vBbZ6Xq*vPwnFK{koOF-)46tJkHbt`_CC z4~~`Y+2)BOl531IJ#YOAt5e85f)%tuz@ip4c$?e*j<+^{78Qn(p*6SlH~cb_wYzPx5QLsf0a_{(8oi)_v$2D9_qHUS znS#W~8YucFs*MdqD!%#701|5{e;~DD{uS=EI3H9Ap~GU1M3Z)YNZ>Qi;Aq{#ZVJO8 zkZ_a8!%)rgo*_inYCPk%r?6Rz^*@$I=u1TC@(p8%JXJG5{>i&cGGi$W`=ovkX|n{5 zNG@FM-Se{a0r>_AZlQ{kc z;$*=H;?31f0JTDAa&-*Rz&Xkz)X+*{FX!Np=|rriNJQduu(;8=`;?G7n!pV{ro*%j z_jT!190rJ5o*7(4HhNQ2yAMaBe-ptSN=qc-eHQ7-`1QRE+kgk0Q#7_{655_0V@?mq2q203()6r6+x2FiJqPX|2^x7}2$3tNsI zn?0;zlfo9k4wg=6!``}M%V9A>27v)~tEs` zOZ=Us)a$C+p_WVFS6eBx_1}#`02qtJ`c5=Krxav*hS1(&vwD7grZ4tZ2tFCF>Aj|m8d`?t9S6@!6^#L z@*k^9m(K|S&7>;w1mNb_%*kx=#$CN0sd;;|kmG+5c6jPe+dYcY8C$$ZUy9mJ|3DMF z>mba?#m=aIh>>CEH8;}79hKz&_TG~AZEG(6?{C6Z^GyBYk0go2y13oxb;^&x#{)Uq L5p5grL8<=&`?GV8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b51ecf755901c5f87d42a5ac6383f09cf93849e0 GIT binary patch literal 2580 zcmZvec{mhm8^*`J?;1koxN0f2g<2pi4E*@Z&v+`|9>@$O&EMG3C_lfzzJ zvxd4v-P$O^^eNjYC&n~aVoTJx94#c5%dl8gZ9+k=B<`k-+NqReUI(_tGS5SXv}G6l zW0ZZ`Rm(wwVtR?Ru{@$o|0=;GRN&cdVVs4CEJ_>kVs7$h7WdY)(Ods->$J_E*NWGR zskGwv>xHwElX-kkmM;7%BW;u5FKjd_;xT_-9Pr2VEmJE?+qUy6zfYrR2-SWHM|)39 z(7(F|Z?5DRHoG`xc*?_}m>m0L0fchz? zD6!QvwO4uu!@g=NyJ^qjzSptG5;c$iezklsRWHU~iVxf8x;6U(z1AmJ#rI7YKwP@s zs+{XAzw1t3dC2uCYMJsz3MpCJ+rD_tMa*$`&WTH6Kvc1+> z({1pokhkX8q6E`zrt{GwaQGX3RnUj17zCtJ^lNO{{&>3Zt8E&+U<;B+gFxDc259D^(kM!xJrMBTd%qO!TYymj+YOB2S zxM4SzfT^((ELg|Bu<`n4mI|B1x|THc8pjjton+iX2J2Rfo8`x~3m?Cx7!96I%eUx) zdC9mT(m_eH%+QBU3lIxe0TKNRT0rUGs{9UIo-l4wyuVZ6lpoJ#*R0+zPQDNGfojCh z?{|D>MkgqLhE!4!Bq5Dk<@p^ZH~68O>8?Z`qLo&*oQEM?#;#+9gJHs*_waMw^mjzT zktU0D1l-xKqmskz#i3h|ubkP^c(S!rpeN5s@d<(X6#N-K-$e0V)ZEa`ATh##gkf1} zaHVD)*&pwvglkHV1kzxQ?~TMXmV5F$+VW352$TKTC==zMC7Y3fsu5bpOrl+L(S6>X z28c-Qq5u$x&Vjld7zk$XMUl`ep7Wy9GkHMiFj{_xIqk`V1g-KTk;mIS(=*q&gpOJ` zFkfMPyDG3By5g7FBwj(_jYYzIg8M}uL@WM?Y~3-WHNuKqGAhXzlQnm`Ws@)(WkEbwIbj=tA5qJ<>9L1wy0pj8jh5_L<%CT|XH{jRB$jv&sR+iZykk zd0menD?JgT)yb7dS6V$_!~Wik zSI}ycDJ^sG+*nN0l`HDs9MJk*Du@Hhgf03IuM($1lX`m0sEoJog)JOz6r5b_tRG$Q zx&I_u(j`*j>Cv-DLa{s$#I_|>Q*;NnoM_`3n`kBS(4z#=n;71U3dAW*QSDA_m~qNVwI~DwVjax~v5b&mMRl zo~G%~HQ;OS(DZ#rXLak%-WN+H2#uLrm$G9sQ#{v|))$A)G z=_wfPc91?bNW1hM=G|)nLs>KahVr{#E+tLFS&=rz8OMhH`bB#NCAEAxNHT8V??HNU z_+FK*TOknbpc1#og;2~9k-EaYMM1c%K}_Pvs&I-=kZBJaX{?43^O?anxl}_dk9T@U z1IdWO+nU59o-e2xp!nHCz{njruqUDSMrFx`63xUUX!l3IXvvq5q=BLPBVEygZz0$| z;l&W)+M5Km<{^_U3F|e())=LKLeBJd*Eb8D+AVOTU-{W7zu6Ncfp6r`94Zma57Wvi zVEzMi05b|Cdaj(~&h$)~b~ik+l68WUUA+3>2$+O2JV~&tieTunq(8n9hd}HdBG>Xdf~OQ9$s@z^)Kr#l zVXtrWv81EeGoVq>TFxCoB<#~n9(ZpLg^;mcX@V?xD2*XPtlf6L|Af~ECryp?caLo< z*9A9h#7RYb`;vW{F$-J{Q5|y9vR`8iWcxF0qq6xf8BNd!D;1y>d6%8&mZt9;5CVdk ze%fu7;oO6P?2(}6Q}Z#?iy zQ2mkeLDwr0T$javy!&D<6ZsPX9^vPY9Yr@}o~P41U7yMa?w^=cD9Sp15z}=50AgrICBg2dTv}+(H$=ZfD06-aj-^Yr;qW={h#y69>C;d8BXbY{rV%AK4?G zwew0e-nlSrc{?yi!Yl5^FeB^qiAjTP_>T%?W=5T&%c{u4vU$wCkzlkG5VC@#Z;|^2 zgih`Q>17{>;gyx?!gATCK+?pdkJ(nLxx%e-&4)befY9Y9*O zBk?X-W9RobdRb*69XSYjS-DvxDv46slDUzr82d6{qokY-9`TVoSgK*^dcrCW7RBMg z!oO~gydfnTA_2yLgrg;uq`)tuF~L^p|5Rt1CV23UFL}85RZqGp)aG2OA144p + enable_old_notifications + notifications_compact_view + 0,1,2 + scale_to_square_image_in_notifications + + prev + next + rewind + forward + smart_rewind_prev + smart_forward_next + play_pause_buffering + play_pause + repeat + shuffle + close + n/a + + notification_slot_0_key + @string/notification_slot_smart_rewind_prev_key + + Rewind / Previous + Previous + Rewind + + + @string/notification_slot_smart_rewind_prev_key + @string/notification_slot_prev_key + @string/notification_slot_rewind_key + + + notification_slot_1_key + @string/notification_slot_play_pause_buffering_key + + Play / Pause / Buffering + Play / Pause + Rewind + + + @string/notification_slot_play_pause_buffering_key + @string/notification_slot_play_pause_key + @string/notification_slot_rewind_key + + + + notification_slot_2_key + @string/notification_slot_smart_forward_next_key + + Forward / Next + Forward + Next + Play / Pause / Buffering + Play / Pause + + + @string/notification_slot_smart_forward_next_key + @string/notification_slot_forward_key + @string/notification_slot_next_key + @string/notification_slot_play_pause_buffering_key + @string/notification_slot_play_pause_key + + + notification_slot_3_key + @string/notification_slot_repeat_key + + Repeat + Shuffle + Previous + Forward + Forward / Next + Rewind + Rewind / Previous + Close + N/A + + + @string/notification_slot_repeat_key + @string/notification_slot_shuffle_key + @string/notification_slot_prev_key + @string/notification_slot_forward_key + @string/notification_slot_smart_forward_next_key + @string/notification_slot_rewind_key + @string/notification_slot_smart_rewind_prev_key + @string/notification_slot_close_key + @string/notification_slot_n_a_key + + + notification_slot_4_key + @string/notification_slot_close_key + + Close + Repeat + Shuffle + Next + Forward + Forward / Next + N/A + + + @string/notification_slot_close_key + @string/notification_slot_repeat_key + @string/notification_slot_shuffle_key + @string/notification_slot_next_key + @string/notification_slot_forward_key + @string/notification_slot_smart_forward_next_key + @string/notification_slot_n_a_key + video_mp4 video_webm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3625e67f4..1d1c67364 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,9 +57,18 @@ Install missing Kore app? org.xbmc.kore Show \"Play with Kodi\" option - Lock screen video thumbnail Display an option to play a video via Kodi media center - A video thumbnail is shown on the lock screen when using the background player + Enable old notifications + This enables the old \"Custom RemoteViews\" notifications. If disabled modern MediaStyle notifications will be used. + Scale image to 1:1 aspect ratio + This will scale the notification image from 16:9 to 1:1 aspect ratio + Notification slot 0 + Notification slot 1 + Notification slot 2 + Notification slot 3 + Notification slot 4 + Notification compact view + Notification slots to show in compact view Audio Default audio format Default video format @@ -129,6 +138,7 @@ Other Debug Updates + Notifications Playing in background Playing in popup mode Queued on background player diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml index 31be267af..a77663843 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -1,38 +1,110 @@ - + android:title="@string/theme_title" + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> + android:title="@string/list_view_mode" + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index d1757919b..0ff43ce90 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -81,13 +81,6 @@ android:summary="@string/show_play_with_kodi_summary" android:title="@string/show_play_with_kodi_title"/> - -