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 000000000..b7c7ffd0e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_close_white_32dp.png differ 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 000000000..ac1dd1a66 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png differ 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 000000000..20b61eaee Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_close_white_32dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png new file mode 100644 index 000000000..0a1d09cbf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png differ 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 000000000..cc1334734 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png differ 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 000000000..dfa434473 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png new file mode 100644 index 000000000..396419219 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png new file mode 100644 index 000000000..1573fb111 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png new file mode 100644 index 000000000..c56b1fcfc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png differ 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 000000000..b51ecf755 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png differ diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 946d586de..e5278e840 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -96,6 +96,113 @@ 144p + 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"/> - -