From 4abf6b2f5cb7fe38b0f3b4cc13e48c46b0725a2e Mon Sep 17 00:00:00 2001 From: cool-student Date: Tue, 3 Mar 2020 00:35:44 +0100 Subject: [PATCH 01/30] Notification Improvements - add MediaStyle notifications for Background and Popup playback - reduce excessive notification updating ( / recreating of Notification.Builder object) when playing background / popup media - add new buffering state indicator (can be disabled) - upscale close icon / downscale replay icon - add notification slot settings - move notification settings to appearance - fix Metadata (song title, artist and album art) sometimes not being set correctly - other misc notification fixes Co-authored-by: wb9688 --- .../newpipe/player/BackgroundPlayer.java | 462 +++--- .../newpipe/player/NotificationUtil.java | 1379 +++++++++++++++++ .../newpipe/player/PopupVideoPlayer.java | 333 ++-- .../player/helper/MediaSessionManager.java | 97 +- .../settings/AppearanceSettingsFragment.java | 15 + .../res/drawable-hdpi/ic_close_white_32dp.png | Bin 0 -> 257 bytes .../drawable-hdpi/ic_replay_white_32dp.png | Bin 0 -> 804 bytes .../res/drawable-mdpi/ic_close_white_32dp.png | Bin 0 -> 542 bytes .../drawable-mdpi/ic_replay_white_32dp.png | Bin 0 -> 583 bytes .../drawable-xhdpi/ic_close_white_32dp.png | Bin 0 -> 666 bytes .../drawable-xhdpi/ic_replay_white_32dp.png | Bin 0 -> 1168 bytes .../drawable-xxhdpi/ic_close_white_32dp.png | Bin 0 -> 436 bytes .../drawable-xxhdpi/ic_replay_white_32dp.png | Bin 0 -> 908 bytes .../drawable-xxxhdpi/ic_close_white_32dp.png | Bin 0 -> 1852 bytes .../drawable-xxxhdpi/ic_replay_white_32dp.png | Bin 0 -> 2580 bytes app/src/main/res/values/settings_keys.xml | 107 ++ app/src/main/res/values/strings.xml | 14 +- app/src/main/res/xml/appearance_settings.xml | 92 +- app/src/main/res/xml/video_audio_settings.xml | 7 - 19 files changed, 2025 insertions(+), 481 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java create mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 943d685b1..efbe2b912 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -19,44 +19,33 @@ package org.schabi.newpipe.player; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import android.view.View; -import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.MediaSourceTag; -import org.schabi.newpipe.util.BitmapUtils; -import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.ThemeHelper; -import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; /** @@ -65,6 +54,9 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; * @author mauriciocolli */ public final class BackgroundPlayer extends Service { + private static final String TAG = "BackgroundPlayer"; + private static final boolean DEBUG = BasePlayer.DEBUG; + public static final String ACTION_CLOSE = "org.schabi.newpipe.player.BackgroundPlayer.CLOSE"; public static final String ACTION_PLAY_PAUSE @@ -79,30 +71,27 @@ public final class BackgroundPlayer extends Service { = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND"; public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD"; + public static final String ACTION_BUFFERING + = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_BUFFERING"; + public static final String ACTION_SHUFFLE + = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_SHUFFLE"; public static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource"; - private static final String TAG = "BackgroundPlayer"; - private static final boolean DEBUG = BasePlayer.DEBUG; - private static final int NOTIFICATION_ID = 123789; - private static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60; + + private BasePlayerImpl basePlayerImpl; + private SharedPreferences sharedPreferences; + + private boolean shouldUpdateOnProgress; // only used for old notifications + private boolean isForwardPressed; + private boolean isRewindPressed; /*////////////////////////////////////////////////////////////////////////// // Service-Activity Binder //////////////////////////////////////////////////////////////////////////*/ - private SharedPreferences sharedPreferences; - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ private PlayerEventListener activityListener; private IBinder mBinder; - private NotificationManager notificationManager; - private NotificationCompat.Builder notBuilder; - private RemoteViews notRemoteView; - private RemoteViews bigNotRemoteView; - private boolean shouldUpdateOnProgress; - private int timesNotificationUpdated; /*////////////////////////////////////////////////////////////////////////// // Service's LifeCycle @@ -113,7 +102,7 @@ public final class BackgroundPlayer extends Service { if (DEBUG) { Log.d(TAG, "onCreate() called"); } - notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); assureCorrectAppLanguage(this); ThemeHelper.setTheme(this); @@ -127,7 +116,7 @@ public final class BackgroundPlayer extends Service { @Override public int onStartCommand(final Intent intent, final int flags, final int startId) { if (DEBUG) { - Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], " + Log.d(TAG, "N_ onStartCommand() called with: intent = [" + intent + "], " + "flags = [" + flags + "], startId = [" + startId + "]"); } basePlayerImpl.handleIntent(intent); @@ -160,7 +149,7 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ private void onClose() { if (DEBUG) { - Log.d(TAG, "onClose() called"); + Log.d(TAG, "N_ onClose() called"); } if (basePlayerImpl != null) { @@ -168,9 +157,8 @@ public final class BackgroundPlayer extends Service { basePlayerImpl.stopActivityBinding(); basePlayerImpl.destroy(); } - if (notificationManager != null) { - notificationManager.cancel(NOTIFICATION_ID); - } + NotificationUtil.getInstance() + .cancelNotification(NotificationUtil.NOTIFICATION_ID_BACKGROUND); mBinder = null; basePlayerImpl = null; @@ -191,168 +179,9 @@ public final class BackgroundPlayer extends Service { } } - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ - - private void resetNotification() { - notBuilder = createNotification(); - timesNotificationUpdated = 0; - } - - private NotificationCompat.Builder createNotification() { - notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_background_notification); - bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_background_notification_expanded); - - setupNotification(notRemoteView); - setupNotification(bigNotRemoteView); - - NotificationCompat.Builder builder = new NotificationCompat - .Builder(this, getString(R.string.notification_channel_id)) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setCustomContentView(notRemoteView) - .setCustomBigContentView(bigNotRemoteView); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - setLockScreenThumbnail(builder); - } - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - builder.setPriority(NotificationCompat.PRIORITY_MAX); - } - return builder; - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private void setLockScreenThumbnail(final NotificationCompat.Builder builder) { - boolean isLockScreenThumbnailEnabled = sharedPreferences.getBoolean( - getString(R.string.enable_lock_screen_video_thumbnail_key), true); - - if (isLockScreenThumbnailEnabled) { - basePlayerImpl.mediaSessionManager.setLockScreenArt( - builder, - getCenteredThumbnailBitmap() - ); - } else { - basePlayerImpl.mediaSessionManager.clearLockScreenArt(builder); - } - } - - @Nullable - private Bitmap getCenteredThumbnailBitmap() { - final int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; - final int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels; - - return BitmapUtils.centerCrop(basePlayerImpl.getThumbnail(), screenWidth, screenHeight); - } - - private void setupNotification(final RemoteViews remoteViews) { - if (basePlayerImpl == null) { - return; - } - - remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); - remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); - - remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationStop, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT)); - - // Starts background player activity -- attempts to unlock lockscreen - final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(this); - remoteViews.setOnClickPendingIntent(R.id.notificationContent, - PendingIntent.getActivity(this, NOTIFICATION_ID, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); - - if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { - remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_previous); - remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_next); - remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT)); - } else { - remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_rewind); - remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_fastforward); - remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, - new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT)); - } - - setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode()); - } - - /** - * Updates the notification, and the play/pause button in it. - * Used for changes on the remoteView - * - * @param drawableId if != -1, sets the drawable with that id on the play/pause button - */ - private synchronized void updateNotification(final int drawableId) { -// if (DEBUG) { -// Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); -// } - if (notBuilder == null) { - return; - } - if (drawableId != -1) { - if (notRemoteView != null) { - notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - } - notificationManager.notify(NOTIFICATION_ID, notBuilder.build()); - timesNotificationUpdated++; - } - - /*////////////////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////////////////*/ - - private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { - switch (repeatMode) { - case Player.REPEAT_MODE_OFF: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_off); - break; - case Player.REPEAT_MODE_ONE: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_one); - break; - case Player.REPEAT_MODE_ALL: - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - R.drawable.exo_controls_repeat_all); - break; - } - } - ////////////////////////////////////////////////////////////////////////// - protected class BasePlayerImpl extends BasePlayer { @NonNull private final AudioPlaybackResolver resolver; - private int cachedDuration; - private String cachedDurationString; BasePlayerImpl(final Context context) { super(context); @@ -367,51 +196,49 @@ public final class BackgroundPlayer extends Service { @Override public void handleIntent(final Intent intent) { super.handleIntent(intent); - - resetNotification(); - if (bigNotRemoteView != null) { - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false); + if (DEBUG) { + Log.d(TAG, "N_ handleIntent()"); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false); - } - startForeground(NOTIFICATION_ID, notBuilder.build()); + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); // false + NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 0, false); + startForeground(NotificationUtil.NOTIFICATION_ID_BACKGROUND, + NotificationUtil.getInstance().notificationBuilder.build()); } /*////////////////////////////////////////////////////////////////////////// // Thumbnail Loading //////////////////////////////////////////////////////////////////////////*/ - private void updateNotificationThumbnail() { - if (basePlayerImpl == null) { - return; - } - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, - basePlayerImpl.getThumbnail()); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, - basePlayerImpl.getThumbnail()); - } - } - @Override public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onLoadingComplete()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); //true + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onLoadingFailed(final String imageUri, final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onLoadingFailed()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences, true); //true + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } /*////////////////////////////////////////////////////////////////////////// @@ -426,6 +253,14 @@ public final class BackgroundPlayer extends Service { @Override public void onShuffleClicked() { super.onShuffleClicked(); + if (DEBUG) { + Log.d(TAG, "N_ onShuffleClicked:"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updatePlayback(); } @@ -440,31 +275,35 @@ public final class BackgroundPlayer extends Service { final int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); - if (!shouldUpdateOnProgress) { - return; - } - if (timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET) { - resetNotification(); + // setMetadata only updates the metadata when any of the metadata keys are null + basePlayerImpl.mediaSessionManager.setMetadata(basePlayerImpl.getVideoTitle(), + basePlayerImpl.getUploaderName(), basePlayerImpl.getThumbnail(), duration); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /*Oreo*/) { - updateNotificationThumbnail(); + boolean areOldNotificationsEnabled = sharedPreferences.getBoolean( + getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + if (!shouldUpdateOnProgress) { + return; } - } - if (bigNotRemoteView != null) { - if (cachedDuration != duration) { - cachedDuration = duration; - cachedDurationString = getTimeString(duration); + if (NotificationUtil.timesNotificationUpdated + > NotificationUtil.NOTIFICATION_UPDATES_BEFORE_RESET) { + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationUtil.getInstance() + .updateOldNotificationsThumbnail(basePlayerImpl); + } } - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, + + NotificationUtil.getInstance().setCachedDuration(currentProgress, duration); + NotificationUtil.getInstance().setProgressbarOnOldNotifications(duration, currentProgress, false); - bigNotRemoteView.setTextViewText(R.id.notificationTime, - getTimeString(currentProgress) + " / " + cachedDurationString); + + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, duration, - currentProgress, false); - } - updateNotification(-1); } @Override @@ -482,12 +321,7 @@ public final class BackgroundPlayer extends Service { @Override public void destroy() { super.destroy(); - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } - if (bigNotRemoteView != null) { - bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } + NotificationUtil.getInstance().unsetImageInOldNotifications(); } /*////////////////////////////////////////////////////////////////////////// @@ -507,8 +341,14 @@ public final class BackgroundPlayer extends Service { @Override public void onRepeatModeChanged(final int i) { - resetNotification(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onRepeatModeChanged()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updatePlayback(); } @@ -518,9 +358,15 @@ public final class BackgroundPlayer extends Service { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(-1); + if (DEBUG) { + Log.d(TAG, "N_ onMetadataChanged()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); updateMetadata(); } @@ -585,31 +431,36 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ @Override - protected void setupBroadcastReceiver(final IntentFilter intentFltr) { - super.setupBroadcastReceiver(intentFltr); - intentFltr.addAction(ACTION_CLOSE); - intentFltr.addAction(ACTION_PLAY_PAUSE); - intentFltr.addAction(ACTION_REPEAT); - intentFltr.addAction(ACTION_PLAY_PREVIOUS); - intentFltr.addAction(ACTION_PLAY_NEXT); - intentFltr.addAction(ACTION_FAST_REWIND); - intentFltr.addAction(ACTION_FAST_FORWARD); + protected void setupBroadcastReceiver(final IntentFilter intentFilter) { + super.setupBroadcastReceiver(intentFilter); + intentFilter.addAction(ACTION_CLOSE); + intentFilter.addAction(ACTION_PLAY_PAUSE); + intentFilter.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_PLAY_PREVIOUS); + intentFilter.addAction(ACTION_PLAY_NEXT); + intentFilter.addAction(ACTION_FAST_REWIND); + intentFilter.addAction(ACTION_FAST_FORWARD); + intentFilter.addAction(ACTION_BUFFERING); + intentFilter.addAction(ACTION_SHUFFLE); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); - intentFltr.addAction(Intent.ACTION_SCREEN_ON); - intentFltr.addAction(Intent.ACTION_SCREEN_OFF); - - intentFltr.addAction(Intent.ACTION_HEADSET_PLUG); + intentFilter.addAction(Intent.ACTION_HEADSET_PLUG); } @Override public void onBroadcastReceived(final Intent intent) { super.onBroadcastReceived(intent); + if (intent == null || intent.getAction() == null) { return; } + if (DEBUG) { - Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]"); + // FIXME remove N_ + Log.d(TAG, "N_ onBroadcastReceived() called with: intent = [" + intent + "]"); } + switch (intent.getAction()) { case ACTION_CLOSE: onClose(); @@ -627,9 +478,11 @@ public final class BackgroundPlayer extends Service { onPlayPrevious(); break; case ACTION_FAST_FORWARD: + isForwardPressed = true; onFastForward(); break; case ACTION_FAST_REWIND: + isRewindPressed = true; onFastRewind(); break; case Intent.ACTION_SCREEN_ON: @@ -638,6 +491,17 @@ public final class BackgroundPlayer extends Service { case Intent.ACTION_SCREEN_OFF: onScreenOnOff(false); break; + case ACTION_BUFFERING: + onBuffering(); + break; + case ACTION_SHUFFLE: + onShuffleClicked(); + break; + case "android.intent.action.HEADSET_PLUG": //FIXME + /*notificationManager.cancel(NOTIFICATION_ID); + mediaSessionManager.dispose(); + mediaSessionManager.enable(getBaseContext(), basePlayerImpl.simpleExoPlayer);*/ + break; } } @@ -645,6 +509,31 @@ public final class BackgroundPlayer extends Service { // States //////////////////////////////////////////////////////////////////////////*/ + @Override + public void onBuffering() { + super.onBuffering(); + if (NotificationUtil.getInstance().notificationSlot0.contains("buffering") + || NotificationUtil.getInstance().notificationSlot1.contains("buffering") + || NotificationUtil.getInstance().notificationSlot2.contains("buffering") + || NotificationUtil.getInstance().notificationSlot3.contains("buffering") + || NotificationUtil.getInstance().notificationSlot4.contains("buffering")) { + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + if (!(isForwardPressed || isRewindPressed)) { + if (DEBUG) { + Log.d(TAG, "N_ onBuffering()"); + } + NotificationUtil.getInstance().updateBackgroundPlayerNotification(-1, + getBaseContext(), basePlayerImpl, sharedPreferences); + } else { + isForwardPressed = false; + isRewindPressed = false; + } + } + } + } + @Override public void changeState(final int state) { super.changeState(state); @@ -654,31 +543,50 @@ public final class BackgroundPlayer extends Service { @Override public void onPlaying() { super.onPlaying(); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(R.drawable.exo_controls_pause); + + if (DEBUG) { + Log.d(TAG, "N_ onPlaying()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_pause_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onPaused() { super.onPaused(); - resetNotification(); - updateNotificationThumbnail(); - updateNotification(R.drawable.exo_controls_play); + + if (DEBUG) { + Log.d(TAG, "N_ onPaused()"); + } + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } @Override public void onCompleted() { super.onCompleted(); - resetNotification(); - if (bigNotRemoteView != null) { - bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false); + if (DEBUG) { + Log.d(TAG, "N_ onCompleted()"); } - if (notRemoteView != null) { - notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false); - } - updateNotificationThumbnail(); - updateNotification(R.drawable.ic_replay_white_24dp); + + NotificationUtil.getInstance().recreateBackgroundPlayerNotification(context, + basePlayerImpl.mediaSessionManager.getSessionToken(), basePlayerImpl, + sharedPreferences); + NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 100, false); + NotificationUtil.getInstance().updateOldNotificationsThumbnail(basePlayerImpl); + NotificationUtil.getInstance() + .updateBackgroundPlayerNotification(R.drawable.ic_replay_white_24dp, + getBaseContext(), basePlayerImpl, sharedPreferences); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java new file mode 100644 index 000000000..68648a8ad --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -0,0 +1,1379 @@ +package org.schabi.newpipe.player; + +import android.annotation.SuppressLint; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.os.Build; +import android.support.v4.media.session.MediaSessionCompat; +import android.util.Log; +import android.widget.RemoteViews; + +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import com.google.android.exoplayer2.Player; + +import org.schabi.newpipe.BuildConfig; +import org.schabi.newpipe.R; +import org.schabi.newpipe.util.NavigationHelper; + +import static android.content.Context.NOTIFICATION_SERVICE; +import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; + +/** + * This is a utility class for player notifications. + * + * @author cool-student + */ +public final class NotificationUtil { + private static final String TAG = "NotificationUtil"; + private static final boolean DEBUG = BasePlayer.DEBUG; + + public static final int NOTIFICATION_ID_BACKGROUND = 123789; + public static final int NOTIFICATION_ID_POPUP = 40028922; + static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60; // only used for old notifications + + static int timesNotificationUpdated; // only used for old notifications + + NotificationCompat.Builder notificationBuilder; + + String notificationSlot0 = "smart_rewind_prev"; + String notificationSlot1 = "play_pause_buffering"; + String notificationSlot2 = "smart_forward_next"; + String notificationSlot3 = "repeat"; + String notificationSlot4 = "close"; + + private NotificationManager notificationManager; + /*private NotificationManager notificationManager; + private NotificationCompat.Builder notificationBuilder;*/ + + private RemoteViews notificationPopupRemoteView; + private RemoteViews notificationRemoteView; // always null when new notifications are used + private RemoteViews bigNotificationRemoteView; // always null when new notifications are used + + private int cachedDuration; // only used for old notifications + private String cachedDurationString; // only used for old notifications + + private NotificationUtil() { } + + public static NotificationUtil getInstance() { + return LazyHolder.INSTANCE; + } + + void recreatePopupPlayerNotification(final Context context, + final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences, + final boolean recreate) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); + } else if (notificationBuilder == null || recreate) { + Log.d(TAG, "N_ recreatePopupPlayerNotification(true)"); + notificationBuilder = createPopupPlayerNotification(context, mediaSessionCompatToken, + playerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreatePopupPlayerNotification(final Context context, + final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); + } else if (notificationBuilder == null) { + Log.d(TAG, "N_ recreatePopupPlayerNotification()"); + notificationBuilder = createPopupPlayerNotification(context, + mediaSessionCompatToken, playerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreateBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences, final boolean recreate) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (notificationBuilder == null || recreate || areOldNotificationsEnabled) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification(true)"); + notificationBuilder = createBackgroundPlayerNotification(context, + mediaSessionCompatToken, basePlayerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + void recreateBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (notificationBuilder == null || areOldNotificationsEnabled) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification()"); + notificationBuilder = createBackgroundPlayerNotification(context, + mediaSessionCompatToken, basePlayerImpl, sharedPreferences); + } + timesNotificationUpdated = 0; + } + + NotificationCompat.Builder createBackgroundPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || areOldNotificationsEnabled) { + notificationRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_background_notification); + bigNotificationRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_background_notification_expanded); + + setupOldNotification(notificationRemoteView, context, basePlayerImpl); + setupOldNotification(bigNotificationRemoteView, context, basePlayerImpl); + + builder + .setOngoing(true) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setCustomContentView(notificationRemoteView) + .setCustomBigContentView(bigNotificationRemoteView) + .setPriority(NotificationCompat.PRIORITY_MAX); + } else { + String compactView = sharedPreferences.getString(context.getString( + R.string.settings_notifications_compact_view_key), "0,1,2"); + int compactSlot0; + int compactSlot1; + int compactSlot2; + try { + String[] parts = compactView.split(","); + compactSlot0 = Integer.parseInt(parts[0]); + compactSlot1 = Integer.parseInt(parts[1]); + compactSlot2 = Integer.parseInt(parts[2]); + if (compactSlot0 > 4) { + compactSlot0 = 0; + } + if (compactSlot1 > 4) { + compactSlot1 = 1; + } + if (compactSlot2 > 4) { + compactSlot2 = 2; + } + } catch (Exception e) { + e.printStackTrace(); + compactSlot0 = 0; + compactSlot1 = 1; + compactSlot2 = 2; + } + + builder + .setStyle(new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionCompatToken) + .setShowCancelButton(false) + .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) + .setOngoing(false) + .setContentIntent(PendingIntent.getActivity(context, NOTIFICATION_ID_BACKGROUND, + new Intent(NavigationHelper.getBackgroundPlayerActivityIntent(context)), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(basePlayerImpl.getVideoTitle()) + .setContentText(basePlayerImpl.getUploaderName()) + .setDeleteIntent(PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setColor(ContextCompat.getColor(context, R.color.gray)) + .setPriority(NotificationCompat.PRIORITY_HIGH); + final boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context + .getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(basePlayerImpl.getThumbnail())); + } else { + builder.setLargeIcon(basePlayerImpl.getThumbnail()); + } + + notificationSlot0 = sharedPreferences.getString( + context.getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = sharedPreferences.getString( + context.getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = sharedPreferences.getString( + context.getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = sharedPreferences.getString( + context.getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = sharedPreferences.getString( + context.getString(R.string.notification_slot_4_key), notificationSlot4); + + addAction(context, builder, basePlayerImpl, notificationSlot0); + addAction(context, builder, basePlayerImpl, notificationSlot1); + addAction(context, builder, basePlayerImpl, notificationSlot2); + addAction(context, builder, basePlayerImpl, notificationSlot3); + addAction(context, builder, basePlayerImpl, notificationSlot4); + } + + return builder; + } + + private void addAction(final Context context, final NotificationCompat.Builder builder, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final String slot) { + switch (slot) { + case "play_pause_buffering": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.ic_file_download_white_24dp, "Buffering", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } else { + builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (basePlayerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + } + break; + case "play_pause": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else if (basePlayerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "rewind": + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_rewind_prev": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "forward": + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_forward_next": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "next": + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "repeat": + switch (basePlayerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + builder.addAction(R.drawable.exo_controls_repeat_one, "RepeatOne", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_ALL: + builder.addAction(R.drawable.exo_controls_repeat_all, "RepeatAll", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_OFF: + default: + builder.addAction(R.drawable.exo_controls_repeat_off, "RepeatOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + break; + case "shuffle": + if (basePlayerImpl.playQueue.isShuffled()) { + builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_shuffle_off, "ShuffleOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "close": + builder.addAction(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + } + + /** + * Updates the notification, and the button icons depending on the playback state. + * On old notifications used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + * @param context + * @param basePlayerImpl + * @param sharedPreferences + */ + synchronized void updateBackgroundPlayerNotification( + final int drawableId, final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final SharedPreferences sharedPreferences) { + if (DEBUG) { + Log.d(TAG, "N_ updateBackgroundPlayerNotification()"); + } + + if (notificationBuilder == null) { + return; + } + if (drawableId != -1) { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView + .setImageViewResource(R.id.notificationPlayPause, drawableId); + } + } + + final boolean areOldNotificationsEnabled = sharedPreferences + .getBoolean(context.getString(R.string.enable_old_notifications_key), false); + if (!areOldNotificationsEnabled) { + notificationBuilder.setContentTitle(basePlayerImpl.getVideoTitle()); + notificationBuilder.setContentText(basePlayerImpl.getUploaderName()); + final boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean( + context.getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + notificationBuilder.setLargeIcon(getBitmapWithSquareAspectRatio(basePlayerImpl + .getThumbnail())); + } else { + notificationBuilder.setLargeIcon(basePlayerImpl.getThumbnail()); + } + + setAction(context, basePlayerImpl, notificationSlot0, 0); + setAction(context, basePlayerImpl, notificationSlot1, 1); + setAction(context, basePlayerImpl, notificationSlot2, 2); + setAction(context, basePlayerImpl, notificationSlot3, 3); + setAction(context, basePlayerImpl, notificationSlot4, 4); + } + + notificationManager.notify(NOTIFICATION_ID_BACKGROUND, notificationBuilder.build()); + + if (areOldNotificationsEnabled) { + timesNotificationUpdated++; + } + } + + @SuppressLint("RestrictedApi") + private void setAction(final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl, + final String slot, final int slotNumber) { + switch (slot) { + case "play_pause_buffering": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_file_download_white_24dp, + "Buffering", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT))); + notificationBuilder.setSmallIcon(android.R.drawable.stat_sys_download); + } else if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (basePlayerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "play_pause": + if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || basePlayerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + if (basePlayerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "rewind": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_rewind_prev": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, + "Prev", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "forward": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_forward_next": + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "next": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "repeat": + switch (basePlayerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_one, + "RepeatOne", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_ALL: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_all, + "RepeatAll", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_OFF: + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_off, + "RepeatOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + break; + case "shuffle": + if (basePlayerImpl.playQueue.isShuffled()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, + "ShuffleOn", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_off, + "ShuffleOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "close": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + } + + private NotificationCompat.Builder createPopupPlayerNotification( + final Context context, final MediaSessionCompat.Token mediaSessionCompatToken, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + String compactView = sharedPreferences.getString(context + .getString(R.string.settings_notifications_compact_view_key), "0,1,2"); + int compactSlot0; + int compactSlot1; + int compactSlot2; + try { + String[] parts = compactView.split(","); + compactSlot0 = Integer.parseInt(parts[0]); + compactSlot1 = Integer.parseInt(parts[1]); + compactSlot2 = Integer.parseInt(parts[2]); + if (compactSlot0 > 4) { + compactSlot0 = 0; + } + if (compactSlot1 > 4) { + compactSlot1 = 1; + } + if (compactSlot2 > 4) { + compactSlot2 = 2; + } + } catch (Exception e) { + e.printStackTrace(); + compactSlot0 = 0; + compactSlot1 = 1; + compactSlot2 = 2; + } + + builder + .setStyle( + new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mediaSessionCompatToken) + .setShowCancelButton(false) + .setShowActionsInCompactView(compactSlot0, compactSlot1, + compactSlot2)) + .setOngoing(false) + .setContentIntent(PendingIntent.getActivity(context, NOTIFICATION_ID_POPUP, + new Intent(NavigationHelper.getPopupPlayerActivityIntent(context)), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(playerImpl.getVideoTitle()) + .setContentText(playerImpl.getUploaderName()) + .setDeleteIntent(PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)) + .setColor(ContextCompat.getColor(context, R.color.gray)); + boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context + .getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(playerImpl.getThumbnail())); + } else { + builder.setLargeIcon(playerImpl.getThumbnail()); + } + notificationSlot0 = sharedPreferences.getString(context + .getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = sharedPreferences.getString(context + .getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = sharedPreferences.getString(context. + getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = sharedPreferences.getString(context + .getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = sharedPreferences.getString(context + .getString(R.string.notification_slot_4_key), notificationSlot4); + + addAction(context, builder, playerImpl, notificationSlot0); + addAction(context, builder, playerImpl, notificationSlot1); + addAction(context, builder, playerImpl, notificationSlot2); + addAction(context, builder, playerImpl, notificationSlot3); + addAction(context, builder, playerImpl, notificationSlot4); + + builder.setPriority(NotificationCompat.PRIORITY_HIGH); + return builder; + } + + private void addAction(final Context context, final NotificationCompat.Builder builder, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, final String slot) { + switch (slot) { + case "play_pause_buffering": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.ic_file_download_white_24dp, "Buffering", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } else { + builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (playerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + } + break; + case "play_pause": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else if (playerImpl.isPlaying()) { + builder.addAction(R.drawable.exo_notification_pause, "Pause", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_notification_play, "Play", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "rewind": + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_rewind_prev": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_rewind, "Rewind", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "forward": + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "smart_forward_next": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_fastforward, "Forward", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "next": + builder.addAction(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "repeat": + switch (playerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + builder.addAction(R.drawable.exo_controls_repeat_one, "RepeatOne", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_ALL: + builder.addAction(R.drawable.exo_controls_repeat_all, "RepeatAll", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case Player.REPEAT_MODE_OFF: + default: + builder.addAction(R.drawable.exo_controls_repeat_off, "RepeatOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + break; + case "shuffle": + if (playerImpl.playQueue.isShuffled()) { + builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.exo_controls_shuffle_off, "ShuffleOff", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + break; + case "close": + builder.addAction(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + case "n/a": + // do nothing + break; + case "prev": + default: + builder.addAction(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + } + + /** + * Updates the notification, and the button icons depending on the playback state. + * On old notifications used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + * @param context + * @param playerImpl + * @param sharedPreferences + */ + @SuppressLint("RestrictedApi") + synchronized void updatePopupPlayerNotification( + final int drawableId, final Context context, + final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final SharedPreferences sharedPreferences) { + if (DEBUG) { + Log.d(TAG, "N_ updatePopupPlayerNotification()"); + } + + if (notificationBuilder == null) { + return; + } + + boolean areOldNotificationsEnabled = sharedPreferences.getBoolean(context + .getString(R.string.enable_old_notifications_key), false); + if (areOldNotificationsEnabled) { + updateOldPopupPlayerNotification(drawableId); + } else { + notificationBuilder.setContentTitle(playerImpl.getVideoTitle()); + notificationBuilder.setContentText(playerImpl.getUploaderName()); + boolean scaleImageToSquareAspectRatio = sharedPreferences.getBoolean(context. + getString(R.string.scale_to_square_image_in_notifications_key), false); + if (scaleImageToSquareAspectRatio) { + notificationBuilder + .setLargeIcon(getBitmapWithSquareAspectRatio(playerImpl.getThumbnail())); + } else { + notificationBuilder.setLargeIcon(playerImpl.getThumbnail()); + } + + setAction(context, playerImpl, notificationSlot0, 0); + setAction(context, playerImpl, notificationSlot1, 1); + setAction(context, playerImpl, notificationSlot2, 2); + setAction(context, playerImpl, notificationSlot3, 3); + setAction(context, playerImpl, notificationSlot4, 4); + } + + notificationManager.notify(NOTIFICATION_ID_POPUP, notificationBuilder.build()); + + if (areOldNotificationsEnabled) { + timesNotificationUpdated++; + } + } + + @SuppressLint("RestrictedApi") + private void setAction(final Context context, final PopupVideoPlayer.VideoPlayerImpl playerImpl, + final String slot, final int slotNumber) { + switch (slot) { + case "play_pause_buffering": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_file_download_white_24dp, + "Buffering", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_BUFFERING), + PendingIntent.FLAG_UPDATE_CURRENT))); + notificationBuilder.setSmallIcon(android.R.drawable.stat_sys_download); + } else if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); + if (playerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "play_pause": + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + "Completed", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + if (playerImpl.isPlaying()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_pause, + "Pause", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_play, + "Play", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + } + break; + case "rewind": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, + "Rewind", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_rewind_prev": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, + "Prev", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_rewind, + "Rewind", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "forward": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "smart_forward_next": + if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_fastforward, + "Forward", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "next": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_next, "Next", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "repeat": + switch (playerImpl.getRepeatMode()) { + case Player.REPEAT_MODE_ONE: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_one, + "RepeatOne", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_ALL: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_all, + "RepeatAll", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case Player.REPEAT_MODE_OFF: + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_repeat_off, + "RepeatOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + break; + case "shuffle": + if (playerImpl.playQueue.isShuffled()) { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, + "ShuffleOn", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } else { + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_controls_shuffle_off, + "ShuffleOff", PendingIntent.getBroadcast(context, + NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_SHUFFLE), + PendingIntent.FLAG_UPDATE_CURRENT))); + } + break; + case "close": + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + case "n/a": // do nothing + /*try { //FIXME maybe do nothing here ? + notificationBuilder.mActions.remove(slotNumber); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + }*/ + break; + case "prev": + default: + notificationBuilder.mActions.set(slotNumber, + new NotificationCompat.Action(R.drawable.exo_notification_previous, "Prev", + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT))); + break; + } + } + + private Bitmap getBitmapWithSquareAspectRatio(final Bitmap bitmap) { + return getResizedBitmap(bitmap, bitmap.getWidth(), bitmap.getWidth()); + } + + private Bitmap getResizedBitmap(final Bitmap bitmap, final int newWidth, final int newHeight) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false); + } + + @Deprecated // only used for old notifications + private void setupOldNotification(final RemoteViews remoteViews, final Context context, + final BackgroundPlayer.BasePlayerImpl basePlayerImpl) { + if (basePlayerImpl == null) { + return; + } + + remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); + remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); + + remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationStop, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + + // Starts background player activity -- attempts to unlock lockscreen + final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(context); + remoteViews.setOnClickPendingIntent(R.id.notificationContent, + PendingIntent.getActivity(context, NOTIFICATION_ID_BACKGROUND, intent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.size() > 1) { + remoteViews.setInt(R.id.notificationFRewind, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous); + remoteViews.setInt(R.id.notificationFForward, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next); + remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_PREVIOUS), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationFForward, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_PLAY_NEXT), + PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + remoteViews.setInt(R.id.notificationFRewind, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_rewind); + remoteViews.setInt(R.id.notificationFForward, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_fastforward); + remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_REWIND), + PendingIntent.FLAG_UPDATE_CURRENT)); + remoteViews.setOnClickPendingIntent(R.id.notificationFForward, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, + new Intent(BackgroundPlayer.ACTION_FAST_FORWARD), + PendingIntent.FLAG_UPDATE_CURRENT)); + } + + setRepeatModeIcon(remoteViews, basePlayerImpl.getRepeatMode()); + } + + @Deprecated // only used for old notifications + private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { + switch (repeatMode) { + case Player.REPEAT_MODE_OFF: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_off); + break; + case Player.REPEAT_MODE_ONE: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_one); + break; + case Player.REPEAT_MODE_ALL: + remoteViews.setInt(R.id.notificationRepeat, + BackgroundPlayer.SET_IMAGE_RESOURCE_METHOD, + R.drawable.exo_controls_repeat_all); + break; + } + } + + @Deprecated // only used for old notifications + public void updateOldNotificationsThumbnail( + final BackgroundPlayer.BasePlayerImpl basePlayerImpl) { + if (basePlayerImpl == null) { + return; + } + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, + basePlayerImpl.getThumbnail()); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView.setImageViewBitmap(R.id.notificationCover, + basePlayerImpl.getThumbnail()); + } + } + + @Deprecated // only used for old notifications + public void setProgressbarOnOldNotifications(final int max, final int progress, + final boolean indeterminate) { + if (bigNotificationRemoteView != null) { //FIXME put in Util and turn into a method + bigNotificationRemoteView.setProgressBar(R.id.notificationProgressBar, max, progress, + indeterminate); + } + if (notificationRemoteView != null) { + notificationRemoteView.setProgressBar(R.id.notificationProgressBar, max, progress, + indeterminate); + } + } + + @Deprecated // only used for old notifications + public void unsetImageInOldNotifications() { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + if (bigNotificationRemoteView != null) { + bigNotificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + } + + @Deprecated // only used for old notifications + public void setCachedDuration(final int currentProgress, final int duration) { + if (bigNotificationRemoteView != null) { + if (cachedDuration != duration) { + cachedDuration = duration; + cachedDurationString = getTimeString(duration); + } + bigNotificationRemoteView.setTextViewText(R.id.notificationTime, + getTimeString(currentProgress) + " / " + cachedDurationString); + } + } + + @Deprecated + private NotificationCompat.Builder createOldPopupPlayerNotification( + final Context context, final PopupVideoPlayer.VideoPlayerImpl playerImpl) { + notificationManager = ((NotificationManager) context + .getSystemService(NOTIFICATION_SERVICE)); + notificationBuilder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)); + + notificationPopupRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, + R.layout.player_popup_notification); + + notificationPopupRemoteView.setTextViewText(R.id.notificationSongName, + playerImpl.getVideoTitle()); + notificationPopupRemoteView.setTextViewText(R.id.notificationArtist, + playerImpl.getUploaderName()); + notificationPopupRemoteView.setImageViewBitmap(R.id.notificationCover, + playerImpl.getThumbnail()); + + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationStop, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_CLOSE), + PendingIntent.FLAG_UPDATE_CURRENT)); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationRepeat, + PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, + new Intent(PopupVideoPlayer.ACTION_REPEAT), + PendingIntent.FLAG_UPDATE_CURRENT)); + + // Starts popup player activity -- attempts to unlock lockscreen + final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(context); + notificationPopupRemoteView.setOnClickPendingIntent(R.id.notificationContent, + PendingIntent.getActivity(context, NOTIFICATION_ID_POPUP, intent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + setRepeatPopupModeRemote(notificationPopupRemoteView, playerImpl.getRepeatMode()); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, + context.getString(R.string.notification_channel_id)) + .setOngoing(true) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContent(notificationPopupRemoteView); + + builder.setPriority(NotificationCompat.PRIORITY_MAX); + return builder; + } + + /* + * Updates the notification, and the play/pause button in it. + * Used for changes on the remoteView + * + * @param drawableId if != -1, sets the drawable with that id on the play/pause button + */ + @Deprecated + private void updateOldPopupPlayerNotification(final int drawableId) { + if (DEBUG) { + Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); + } + if (notificationBuilder == null || notificationPopupRemoteView == null) { + return; + } + if (drawableId != -1) { + notificationPopupRemoteView.setImageViewResource(R.id.notificationPlayPause, + drawableId); + } + notificationManager.notify(NOTIFICATION_ID_POPUP, notificationBuilder.build()); + } + + @Deprecated // only used for old notifications + protected void setRepeatPopupModeRemote(final RemoteViews remoteViews, final int repeatMode) { + final String methodName = "setImageResource"; + + if (remoteViews == null) { + return; + } + + switch (repeatMode) { + case Player.REPEAT_MODE_OFF: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_off); + break; + case Player.REPEAT_MODE_ONE: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_one); + break; + case Player.REPEAT_MODE_ALL: + remoteViews.setInt(R.id.notificationRepeat, methodName, + R.drawable.exo_controls_repeat_all); + break; + } + } + + @Deprecated // only used for old notifications + public void unsetImageInOldPopupNotifications() { + if (notificationRemoteView != null) { + notificationRemoteView.setImageViewBitmap(R.id.notificationCover, null); + } + } + + public void cancelNotification(final int id) { + try { + if (notificationManager != null) { + notificationManager.cancel(id); + } + } catch (Exception e) { + Log.e("NotificationUtil", "Exception", e); + } + } + + private static class LazyHolder { + private static final NotificationUtil INSTANCE = new NotificationUtil(); + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 0ccec3067..a1ad139f6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -22,8 +22,6 @@ package org.schabi.newpipe.player; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -48,23 +46,19 @@ import android.view.animation.AnticipateInterpolator; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.PopupMenu; -import android.widget.RemoteViews; import android.widget.SeekBar; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.core.app.NotificationCompat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.text.CaptionStyleCompat; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.player.event.PlayerEventListener; @@ -89,13 +83,28 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; * @author mauriciocolli */ public final class PopupVideoPlayer extends Service { - public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE"; - public static final String ACTION_PLAY_PAUSE - = "org.schabi.newpipe.player.PopupVideoPlayer.PLAY_PAUSE"; - public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT"; private static final String TAG = ".PopupVideoPlayer"; private static final boolean DEBUG = BasePlayer.DEBUG; - private static final int NOTIFICATION_ID = 40028922; + + public static final String ACTION_CLOSE + = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE"; + public static final String ACTION_PLAY_PAUSE + = "org.schabi.newpipe.player.PopupVideoPlayer.PLAY_PAUSE"; + public static final String ACTION_REPEAT + = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT"; + public static final String ACTION_FAST_REWIND + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_FAST_REWIND"; + public static final String ACTION_FAST_FORWARD + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_FAST_FORWARD"; + public static final String ACTION_PLAY_NEXT + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_PLAY_NEXT"; + public static final String ACTION_PLAY_PREVIOUS + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_PLAY_PREVIOUS"; + public static final String ACTION_BUFFERING + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_BUFFERING"; + public static final String ACTION_SHUFFLE + = "org.schabi.newpipe.player.PopupVideoPlayer.ACTION_SHUFFLE"; + private static final String POPUP_SAVED_WIDTH = "popup_saved_width"; private static final String POPUP_SAVED_X = "popup_saved_x"; private static final String POPUP_SAVED_Y = "popup_saved_y"; @@ -126,12 +135,12 @@ public final class PopupVideoPlayer extends Service { private float maximumWidth; private float maximumHeight; - private NotificationManager notificationManager; - private NotificationCompat.Builder notBuilder; - private RemoteViews notRemoteView; + private boolean isForwardPressed; + private boolean isRewindPressed; private VideoPlayerImpl playerImpl; private boolean isPopupClosing = false; + private SharedPreferences sharedPreferences; /*////////////////////////////////////////////////////////////////////////// // Service-Activity Binder @@ -148,7 +157,7 @@ public final class PopupVideoPlayer extends Service { public void onCreate() { assureCorrectAppLanguage(this); windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); - notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); playerImpl = new VideoPlayerImpl(this); ThemeHelper.setTheme(this); @@ -220,9 +229,9 @@ public final class PopupVideoPlayer extends Service { final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(this); final float defaultSize = getResources().getDimension(R.dimen.popup_default_width); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); popupWidth = popupRememberSizeAndPos - ? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize; + ? sharedPrefs.getFloat(POPUP_SAVED_WIDTH, defaultSize) : defaultSize; final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_PHONE @@ -236,16 +245,16 @@ public final class PopupVideoPlayer extends Service { popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - int centerX = (int) (screenWidth / 2f - popupWidth / 2f); - int centerY = (int) (screenHeight / 2f - popupHeight / 2f); + final int centerX = (int) (screenWidth / 2f - popupWidth / 2f); + final int centerY = (int) (screenHeight / 2f - popupHeight / 2f); popupLayoutParams.x = popupRememberSizeAndPos - ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX; + ? sharedPrefs.getInt(POPUP_SAVED_X, centerX) : centerX; popupLayoutParams.y = popupRememberSizeAndPos - ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY; + ? sharedPrefs.getInt(POPUP_SAVED_Y, centerY) : centerY; checkPopupPositionBounds(); - PopupWindowGestureListener listener = new PopupWindowGestureListener(); + final PopupWindowGestureListener listener = new PopupWindowGestureListener(); popupGestureDetector = new GestureDetector(this, listener); rootView.setOnTouchListener(listener); @@ -282,71 +291,6 @@ public final class PopupVideoPlayer extends Service { windowManager.addView(closeOverlayView, closeOverlayLayoutParams); } - /*////////////////////////////////////////////////////////////////////////// - // Notification - //////////////////////////////////////////////////////////////////////////*/ - - private void resetNotification() { - notBuilder = createNotification(); - } - - private NotificationCompat.Builder createNotification() { - notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_popup_notification); - - notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle()); - notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName()); - notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail()); - - notRemoteView.setOnClickPendingIntent(R.id.notificationPlayPause, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), - PendingIntent.FLAG_UPDATE_CURRENT)); - notRemoteView.setOnClickPendingIntent(R.id.notificationStop, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), - PendingIntent.FLAG_UPDATE_CURRENT)); - notRemoteView.setOnClickPendingIntent(R.id.notificationRepeat, - PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), - PendingIntent.FLAG_UPDATE_CURRENT)); - - // Starts popup player activity -- attempts to unlock lockscreen - final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(this); - notRemoteView.setOnClickPendingIntent(R.id.notificationContent, - PendingIntent.getActivity(this, NOTIFICATION_ID, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); - - setRepeatModeRemote(notRemoteView, playerImpl.getRepeatMode()); - - NotificationCompat.Builder builder = new NotificationCompat - .Builder(this, getString(R.string.notification_channel_id)) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContent(notRemoteView); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - builder.setPriority(NotificationCompat.PRIORITY_MAX); - } - return builder; - } - - /** - * Updates the notification, and the play/pause button in it. - * Used for changes on the remoteView - * - * @param drawableId if != -1, sets the drawable with that id on the play/pause button - */ - private void updateNotification(final int drawableId) { - if (DEBUG) { - Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); - } - if (notBuilder == null || notRemoteView == null) { - return; - } - if (drawableId != -1) { - notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); - } - notificationManager.notify(NOTIFICATION_ID, notBuilder.build()); - } - /*////////////////////////////////////////////////////////////////////////// // Misc //////////////////////////////////////////////////////////////////////////*/ @@ -372,9 +316,8 @@ public final class PopupVideoPlayer extends Service { } mBinder = null; - if (notificationManager != null) { - notificationManager.cancel(NOTIFICATION_ID); - } + + NotificationUtil.getInstance().cancelNotification(NotificationUtil.NOTIFICATION_ID_POPUP); animateOverlayAndFinishService(); } @@ -461,11 +404,11 @@ public final class PopupVideoPlayer extends Service { } private void savePositionAndSize() { - SharedPreferences sharedPreferences = PreferenceManager + final SharedPreferences sharedPrefs = PreferenceManager .getDefaultSharedPreferences(PopupVideoPlayer.this); - sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply(); - sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply(); - sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply(); + sharedPrefs.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply(); + sharedPrefs.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply(); + sharedPrefs.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply(); } private float getMinimumVideoHeight(final float width) { @@ -530,29 +473,6 @@ public final class PopupVideoPlayer extends Service { windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams); } - protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) { - final String methodName = "setImageResource"; - - if (remoteViews == null) { - return; - } - - switch (repeatMode) { - case Player.REPEAT_MODE_OFF: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_off); - break; - case Player.REPEAT_MODE_ONE: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_one); - break; - case Player.REPEAT_MODE_ALL: - remoteViews.setInt(R.id.notificationRepeat, methodName, - R.drawable.exo_controls_repeat_all); - break; - } - } - private void updateWindowFlags(final int flags) { if (popupLayoutParams == null || windowManager == null || playerImpl == null) { return; @@ -579,8 +499,11 @@ public final class PopupVideoPlayer extends Service { public void handleIntent(final Intent intent) { super.handleIntent(intent); - resetNotification(); - startForeground(NOTIFICATION_ID, notBuilder.build()); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + startForeground(NotificationUtil.NOTIFICATION_ID_POPUP, + NotificationUtil.getInstance().notificationBuilder.build()); } @Override @@ -622,9 +545,7 @@ public final class PopupVideoPlayer extends Service { @Override public void destroy() { - if (notRemoteView != null) { - notRemoteView.setImageViewBitmap(R.id.notificationCover, null); - } + NotificationUtil.getInstance().unsetImageInOldPopupNotifications(); super.destroy(); } @@ -683,6 +604,11 @@ public final class PopupVideoPlayer extends Service { @Override public void onShuffleClicked() { super.onShuffleClicked(); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); updatePlayback(); } @@ -697,6 +623,10 @@ public final class PopupVideoPlayer extends Service { final int bufferPercent) { updateProgress(currentProgress, duration, bufferPercent); super.onUpdateProgress(currentProgress, duration, bufferPercent); + + // setMetadata only updates the metadata when any of the metadata keys are null + playerImpl.mediaSessionManager.setMetadata(playerImpl.getVideoTitle(), + playerImpl.getUploaderName(), playerImpl.getThumbnail(), duration); } @Override @@ -724,28 +654,38 @@ public final class PopupVideoPlayer extends Service { public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); + if (playerImpl == null) { return; } - // rebuild notification here since remote view does not release bitmaps, + // rebuild (old) notification here since remote view does not release bitmaps, // causing memory leaks - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override public void onLoadingFailed(final String imageUri, final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override public void onLoadingCancelled(final String imageUri, final View view) { super.onLoadingCancelled(imageUri, view); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, sharedPreferences, + true); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } /*////////////////////////////////////////////////////////////////////////// @@ -799,10 +739,12 @@ public final class PopupVideoPlayer extends Service { @Override public void onRepeatModeChanged(final int i) { super.onRepeatModeChanged(i); - setRepeatModeRemote(notRemoteView, i); updatePlayback(); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); } @Override @@ -817,8 +759,11 @@ public final class PopupVideoPlayer extends Service { protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); - resetNotification(); - updateNotification(-1); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance().updatePopupPlayerNotification(-1, + getBaseContext(), playerImpl, sharedPreferences); updateMetadata(); } @@ -833,18 +778,25 @@ public final class PopupVideoPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ @Override - protected void setupBroadcastReceiver(final IntentFilter intentFltr) { - super.setupBroadcastReceiver(intentFltr); + + protected void setupBroadcastReceiver(final IntentFilter intentFilter) { + super.setupBroadcastReceiver(intentFilter); if (DEBUG) { Log.d(TAG, "setupBroadcastReceiver() called with: " - + "intentFilter = [" + intentFltr + "]"); + + "intentFilter = [" + intentFilter + "]"); } - intentFltr.addAction(ACTION_CLOSE); - intentFltr.addAction(ACTION_PLAY_PAUSE); - intentFltr.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_CLOSE); + intentFilter.addAction(ACTION_PLAY_PAUSE); + intentFilter.addAction(ACTION_REPEAT); + intentFilter.addAction(ACTION_PLAY_PREVIOUS); + intentFilter.addAction(ACTION_PLAY_NEXT); + intentFilter.addAction(ACTION_FAST_REWIND); + intentFilter.addAction(ACTION_FAST_FORWARD); + intentFilter.addAction(ACTION_BUFFERING); + intentFilter.addAction(ACTION_SHUFFLE); - intentFltr.addAction(Intent.ACTION_SCREEN_ON); - intentFltr.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); } @Override @@ -872,6 +824,26 @@ public final class PopupVideoPlayer extends Service { case Intent.ACTION_SCREEN_OFF: enableVideoRenderer(false); break; + case ACTION_PLAY_NEXT: + onPlayNext(); + break; + case ACTION_PLAY_PREVIOUS: + onPlayPrevious(); + break; + case ACTION_FAST_FORWARD: + isForwardPressed = true; + onFastForward(); + break; + case ACTION_FAST_REWIND: + isRewindPressed = true; + onFastRewind(); + break; + case ACTION_BUFFERING: + onBuffering(); + break; + case ACTION_SHUFFLE: + onShuffleClicked(); + break; } } @@ -888,8 +860,13 @@ public final class PopupVideoPlayer extends Service { @Override public void onBlocked() { super.onBlocked(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); } @Override @@ -898,20 +875,48 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.exo_controls_pause); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_pause_white_24dp, getBaseContext(), + playerImpl, sharedPreferences); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_pause); hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - startForeground(NOTIFICATION_ID, notBuilder.build()); + startForeground(NotificationUtil.NOTIFICATION_ID_POPUP, + NotificationUtil.getInstance().notificationBuilder.build()); } @Override public void onBuffering() { super.onBuffering(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + if (NotificationUtil.getInstance().notificationSlot0.contains("buffering") + || NotificationUtil.getInstance().notificationSlot1.contains("buffering") + || NotificationUtil.getInstance().notificationSlot2.contains("buffering") + || NotificationUtil.getInstance().notificationSlot3.contains("buffering") + || NotificationUtil.getInstance().notificationSlot4.contains("buffering")) { + if (playerImpl.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED + || playerImpl.getCurrentState() == BasePlayer.STATE_BUFFERING) { + if (!(isForwardPressed || isRewindPressed)) { + if (DEBUG) { + Log.d(TAG, "N_ onBuffering()"); + } + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + } else { + isForwardPressed = false; + isRewindPressed = false; + } + } + } } @Override @@ -920,9 +925,14 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); - videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + + videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white_24dp); stopForeground(false); } @@ -930,8 +940,13 @@ public final class PopupVideoPlayer extends Service { @Override public void onPausedSeek() { super.onPausedSeek(); - resetNotification(); - updateNotification(R.drawable.exo_controls_play); + + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_play_arrow_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play); } @@ -942,8 +957,13 @@ public final class PopupVideoPlayer extends Service { updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(); - updateNotification(R.drawable.ic_replay_white_24dp); + NotificationUtil.getInstance().recreatePopupPlayerNotification(context, + playerImpl.mediaSessionManager.getSessionToken(), playerImpl, + sharedPreferences); + NotificationUtil.getInstance() + .updatePopupPlayerNotification(R.drawable.ic_replay_white_24dp, + getBaseContext(), playerImpl, sharedPreferences); + videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white_24dp); stopForeground(false); @@ -1004,7 +1024,6 @@ public final class PopupVideoPlayer extends Service { private float initSecPointerX = -1; private float initSecPointerY = -1; - @Override public boolean onDoubleTap(final MotionEvent e) { if (DEBUG) { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java index e101e2185..d0939a914 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/MediaSessionManager.java @@ -3,17 +3,14 @@ package org.schabi.newpipe.player.helper; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.media.MediaMetadata; -import android.os.Build; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; +import android.util.Log; import android.view.KeyEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; -import androidx.media.app.NotificationCompat.MediaStyle; import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.Player; @@ -30,13 +27,29 @@ public class MediaSessionManager { private final MediaSessionCompat mediaSession; @NonNull private final MediaSessionConnector sessionConnector; + @NonNull + private final PlaybackStateCompat.Builder playbackStateCompatBuilder; + + private int tmpThumbHash; public MediaSessionManager(@NonNull final Context context, @NonNull final Player player, @NonNull final MediaSessionCallback callback) { this.mediaSession = new MediaSessionCompat(context, TAG); + this.mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS + | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); this.mediaSession.setActive(true); + this.playbackStateCompatBuilder = new PlaybackStateCompat.Builder(); + this.playbackStateCompatBuilder.setState(PlaybackStateCompat.STATE_NONE, -1, 1); + this.playbackStateCompatBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO + | PlaybackStateCompat.ACTION_PLAY + | PlaybackStateCompat.ACTION_PAUSE // was play and pause now play/pause + | PlaybackStateCompat.ACTION_SKIP_TO_NEXT + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_SET_REPEAT_MODE | PlaybackStateCompat.ACTION_STOP); + this.mediaSession.setPlaybackState(playbackStateCompatBuilder.build()); + this.sessionConnector = new MediaSessionConnector(mediaSession); this.sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback)); this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback)); @@ -49,37 +62,65 @@ public class MediaSessionManager { return MediaButtonReceiver.handleIntent(mediaSession, intent); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public void setLockScreenArt(final NotificationCompat.Builder builder, - @Nullable final Bitmap thumbnailBitmap) { - if (thumbnailBitmap == null || !mediaSession.isActive()) { + public MediaSessionCompat.Token getSessionToken() { + return this.mediaSession.getSessionToken(); + } + + public void setMetadata(final String title, final String artist, final Bitmap albumArt, + final long duration) { + if (albumArt == null || !mediaSession.isActive()) { return; } - mediaSession.setMetadata( - new MediaMetadataCompat.Builder() - .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, thumbnailBitmap) - .build() - ); + if (getMetadataAlbumArt() == null) { + Log.d(TAG, "N_getMetadataAlbumArt: thumb == null"); + } + if (getMetadataTitle() == null) { + Log.d(TAG, "N_getMetadataTitle: title == null"); + } + if (getMetadataArtist() == null) { + Log.d(TAG, "N_getMetadataArtist: artist == null"); + } + if (getMetadataDuration() <= 1) { + Log.d(TAG, "N_getMetadataDuration: duration <= 1; " + getMetadataDuration()); + } - MediaStyle mediaStyle = new MediaStyle() - .setMediaSession(mediaSession.getSessionToken()); - - builder.setStyle(mediaStyle); + if (getMetadataAlbumArt() == null || getMetadataTitle() == null + || getMetadataArtist() == null || getMetadataDuration() <= 1 + || albumArt.hashCode() != tmpThumbHash) { + Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist + + " thumb: " + albumArt.hashCode() + " d: " + duration); + mediaSession.setMetadata( + new MediaMetadataCompat.Builder() + .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) + .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) + .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, albumArt) + .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration) + .build() + ); + tmpThumbHash = albumArt.hashCode(); + } } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public void clearLockScreenArt(final NotificationCompat.Builder builder) { - mediaSession.setMetadata( - new MediaMetadataCompat.Builder() - .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null) - .build() - ); + private Bitmap getMetadataAlbumArt() { + return mediaSession.getController().getMetadata() + .getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART); + } - MediaStyle mediaStyle = new MediaStyle() - .setMediaSession(mediaSession.getSessionToken()); + private String getMetadataTitle() { + return mediaSession.getController().getMetadata() + .getString(MediaMetadataCompat.METADATA_KEY_TITLE); + } - builder.setStyle(mediaStyle); + private String getMetadataArtist() { + return mediaSession.getController().getMetadata() + .getString(MediaMetadataCompat.METADATA_KEY_ARTIST); + } + + private long getMetadataDuration() { + return mediaSession.getController().getMetadata() + .getLong(MediaMetadataCompat.METADATA_KEY_DURATION); } /** diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index a9531693c..c9367c4c9 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -11,6 +11,7 @@ import androidx.annotation.Nullable; import androidx.preference.Preference; import org.schabi.newpipe.R; +import org.schabi.newpipe.player.NotificationUtil; import org.schabi.newpipe.util.Constants; public class AppearanceSettingsFragment extends BasePreferenceFragment { @@ -52,8 +53,22 @@ public class AppearanceSettingsFragment extends BasePreferenceFragment { final Preference captionSettings = findPreference(captionSettingsKey); getPreferenceScreen().removePreference(captionSettings); } + + findPreference(getString(R.string.enable_old_notifications_key)) + .setOnPreferenceChangeListener(oldNotificationsOnPreferenceChangeListener); } + private Preference.OnPreferenceChangeListener oldNotificationsOnPreferenceChangeListener + = (preference, newValue) -> { +// NotificationUtil.getInstance().toast(getContext(), +// "Killed background / popup player notification(s) !"); + NotificationUtil.getInstance() + .cancelNotification(NotificationUtil.NOTIFICATION_ID_BACKGROUND); + NotificationUtil.getInstance().cancelNotification(NotificationUtil.NOTIFICATION_ID_POPUP); + + return true; + }; + @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResource(R.xml.appearance_settings); diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b7c7ffd0e795ba76ed3a062566c9016448795f7a GIT binary patch literal 257 zcmV+c0sj7pP) zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1dd1a6659904ba44561dbfe1b3b3caed950058 GIT binary patch literal 804 zcmV+<1Ka$GP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=Y>Ua!!*MzB~-b zcPe>dSN|c`ndF7t{f0bqlNT=1U&ygAdEsLHg#6|vZ+@fC=nZ;~4$-zlEDw?>lY9~1 z(FxjhxXN6x?V3yIA$m_Ej?iX&wQH!j&RfW$hU_?Ow@~q-My#W2=pK5EKA=3#(4NC~ z3l$eO!bMWJw1)Q4sr&(IM8DpI-810YLNDS%)2KW3XH3|Y3+@O#jSEh5D)|Lordfoj9<&It1B9TGq4d+uaz%JeY(|X>Kb)ARWN7 z=t*taT^q$h9~C&mvEB6{*e?1Nhy91Pz_r&A#Xv6=FoU+!&7RB%sILbhI*e3qgziPT zab2W22)c=M+zZj0ICpo_Jt%zg;vLdwm*yex4${FpL}Nnx`GvES${-Ecu+NK6=!T27 z0~PF{yhaF3vDy;fN7|TeSo*DN^5uPv5Z!%Np(4GByzW#Lb(?mdd(AdV#S~tjs60{lf39H8_eGT0000& literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..20b61eaeec893baf305a701f78fc9c18eee0217d GIT binary patch literal 542 zcmV+(0^$9MP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0kcU&K~z{r?UqYU z0zni-(V?J2*o-c~&pJf+060|H7;Zoqh$HzVO3FzR68}UcLM3 z)Ag!L1qu{M!fZA>fJd4-emjh9!LfZi(y}={aMTvo;Shm{vJab7_dH|;*vs$=KH5hUmqyJ)vH?d4 zHeug_11Q0jZ-PEe_yUeR_5o_bsgEue5`tkTCQy?o+Z-mU;1iE|f(@JV!vPdD1kl#D zY?fVT9#;NLaZWj(;8uWO6>61XC%W(!wqehG0|(E1ZBqeZ?SxjV;2-dnfNzxV@E$g3 z$9|++1A*EK48K8pN^xUzChWTW8(^j3p7)@RU}~zaBS_2>xq$fcJy*bMZ9QezeX)5W z7Z6Om=hPST#0&h9CvpMJklSPLx$1B^Po#P~5`k6)p~c)Dd(YJeHHpQyqeXSIRE%>t z^RWe$+Y|4(Y>d%vg|}k_rIGzH!hiWWPLPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0o+MMK~z{r?Up@D z!%!548!hOdv<0i9j_RP`p!f%L7VYNl>gJ$>n}a{YNjEzRx;O|fej#-eoI6y74t@~d z_vTGWNMq9W8W8%x!%fb)=bUS;iMfA`>$=Oj%pMMhue!|^e>ij%%8uZ0qHs1~UT8Q` z>hHm<$Z)X+nH$ws`)EMB;Xyv(Ijq0|mD&Lf(;OhkHJnws9ndiD5SwrhUche##5-J3 zxgF3jatNgixSfV)CgIV9OG?`T4MT@exCKHEJTyss?9ni3K2!%p043C%>`%2jZ zP0b;68v(fztV68~#%@@`7899Q#cuHrnjcdb-&<(QmJ8$Y6*KOlM)ncm{N}#EGd-;_o~KcU8(e-h*nLx z#|@lRIu`EHUY}E_Y=@w*2p`x*@cF+B$F-#eViSTtVCW^hrT3_XEl~ V_uUT1{e}Pl002ovPDHLkV1lm9{aOG3 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..cc13347341479e29fc567f6af466875e91e4048c GIT binary patch literal 666 zcmV;L0%iS)P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0xwBKK~#8N?V4du z0x=MU(H~w16Yv6_0Zy%_&_u7GzwmozAY|O_E^D{6?0m^fgmydAz6TQ6RS*P05ClOG z1VJ!?!{P9Vp6y{VR1>Z3VRpSaWP{-OhVxDoYYH$L4lXpYtA?o0J4SXJc(S@1e;_ zNzijP)`c=ejLr> zyoWW1l2Go)QHrgRev)sbHb5XocB<}CqX@IY`7d0)h#kQ zglpd-vq5(@Tpo%q^Cqbv2!bF8f*=Tjc3G`{0C;dH`T`V^N&o-=07*qoM6N<$f@YH? A5dZ)H literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa4344735f31f4726c06f74a8282554c0eec7a2 GIT binary patch literal 1168 zcmV;B1aJF^P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1SLsCK~#8N?V8_< z6=fL5_efgpvvr?4ngx z5WHCxL7{9>Q7{wwwK@B+;?YU%&MqDmkEV2J_AtG8Lx*Pv)Z13Pp_lp}YITY?^m2bgo!;UNx%4m8 zm|DCcxBi6sIu>upwf~^D%;F8X_ZQTat9V00YNI7v8q{PzZ;zri(Sd9ZqC4nMbP=6F z=UT1SB(}G(;!&=5K7(86JG2o!q3OehM9V5Z$rZE*J*`lsGDkLKmZC4w1)8uSIua@* zSkw~pZU6#6O} z;O8^Bh6xF#HbE|#;Mzi8M1x!&pcQfr6B6_`K`-Y5cWc7IXrRk+xrGZ!I!(|^xnamW z^jkF8W|DhRl#kUQ#&>@Gp!2cokdv(!K8W|)*g{T+ zv|SDa`vCc^owhnx;El0~W7r7h2MdcyJp4ucCmaPL{>~V6<{Zy&w*Wjq^bk=dv8Payw zAm+=MweemX$7FL+v;n@~**3=~@n%=qjePkRx)&$}UAG$$dqR`sZ`8GQdSVTtI~F*> zc_gojAi%CDl*{*WsT^V*ibFc=luLCGty(ibwV9xh7w~t>@>3{a<2my!CO^fcq*6H`6 zqTezOHV66?*?OOk8^~_dx(|VEbvErIY1kvJRdO#3V^?dAG%WU8a<2yx*oN#K@5C=? zGn%VmshE#^ZA>RD=2i`xF{PY0Br<~e7G+(9?;=|XK8q&MqcuTW99@Q9M{l64TkLyi zJ3o1UjCTWoCagmCf1awjj8-aaSdiF!v>*M4%EspW8qHU@a3RUH=maVYo9j(Y-#d`B zHR2m&_Xn6?(TivvGVGtC*OC2G^fWqw#<32ETvhYyB39D^lU%DgQgv&#U%8l^-CA%qY@2qA=!B5=cm zUgehujQG*l{{`@rPr!f|fEk^>KI9WteE@ilV6HEl&_rJ@p_zU*;T}RirIc{5NocNLm*7JGdV(AM zYYDFOvk5~8{Z*t_>U=!XvoehUSEh=adIga45oe)B9kGgT}7UT3Cirmr(oHPv^YQ1-p=Hlh5u;xggf zX{&yw+El-OrrKQJRl@b7x{HLmNkj95`a#LLnW{Veb2C+!`ppt#r)=g4@?c8RY{yJj~W@W|h^mU4q`i)2y~Rw@J`jIh$1%|In!~{Tb{nMqaxlgb+dq eA%qY@zJ@mriVM?qfwL0;00007!(!0)U7tJ0c*i|=W=`GD{Rhrh*S+VSbLJ}_yb|Y} z2=nyPP+9@g9^_WikSR{kYsy-hy zEku#&*XNV6oCC6@Y0~GT-ej9>Czv`dtH=_DbXwRliC~qaN~fj83SwBz>9x4qN(Rvr ze!Z60m#~`DYlFi_98nBIuPvr=tUC1CrC;Y$3W(^FgzQC`p+k!}pOw6jBq=iPfkh0TWmS)dUz^Bf7(L-mV&-4}J$XOF7sBlc6Scm>hhDc(^Uj{LXd-%JWIiC2AM`rMs#_tFGOcEm> zQ6{J?t)HM6bg;rDU%4Peu#|sCR!~qE~FTC?knj22pV}>5;)E~ds^l}5-05`x5 ia0A=`H^2>0aQz3T@7w=K#~w`p0000Rol24V(Ul{cf{Ft-O^TClm@M})Kx4+>gFppf_Al9 z#ZupGE6zrcR@~QaqeT$ZRuoaI`|%I#4>R+;&olGR%=^sqOcse~F9%hJfcj#!iW);ZTYO z>T=`0X?%9WrcrsC_4A<7*sb<(ZqAXCz$&?zvtCH)DOTz*!{g)CUw2Q0|C}824PDMP z{$6;es_Q`y_y6;2&Fj*OPLzVPYLCjEjk4Z}J3<9+DeG=LyRliiHMy^7kYa3n-Q4^r z=Je=Wh=B)$5psZ;8FFDrR_u@>qb1o1CW4Lrhu0eYPa_i2OYrb(DZ%v))2sppo^;_8 zwb0{z5aLC)COh6l@b#o`w)J1gE$qmZkTWeSW5#}~Xiul~Dxm4=GI?L5oo$KYsh#st zwF=q4KstT`?fKPzz&H~P!{@!{OvE10md89lIPwS=WZaj)>KZoRD~V{@46XE6=K{7Z z#TJUPzMa;tA4a)CC9urOT$T|k$R(Y8;K}A59pxC00f8W^h%BO>TYr4?7hHASPMEOW+xq zFH~-%I>LxXKFc)6rpyej6+LE67n2X3+)vBbZ6Xq*vPwnFK{koOF-)46tJkHbt`_CC z4~~`Y+2)BOl531IJ#YOAt5e85f)%tuz@ip4c$?e*j<+^{78Qn(p*6SlH~cb_wYzPx5QLsf0a_{(8oi)_v$2D9_qHUS znS#W~8YucFs*MdqD!%#701|5{e;~DD{uS=EI3H9Ap~GU1M3Z)YNZ>Qi;Aq{#ZVJO8 zkZ_a8!%)rgo*_inYCPk%r?6Rz^*@$I=u1TC@(p8%JXJG5{>i&cGGi$W`=ovkX|n{5 zNG@FM-Se{a0r>_AZlQ{kc z;$*=H;?31f0JTDAa&-*Rz&Xkz)X+*{FX!Np=|rriNJQduu(;8=`;?G7n!pV{ro*%j z_jT!190rJ5o*7(4HhNQ2yAMaBe-ptSN=qc-eHQ7-`1QRE+kgk0Q#7_{655_0V@?mq2q203()6r6+x2FiJqPX|2^x7}2$3tNsI zn?0;zlfo9k4wg=6!``}M%V9A>27v)~tEs` zOZ=Us)a$C+p_WVFS6eBx_1}#`02qtJ`c5=Krxav*hS1(&vwD7grZ4tZ2tFCF>Aj|m8d`?t9S6@!6^#L z@*k^9m(K|S&7>;w1mNb_%*kx=#$CN0sd;;|kmG+5c6jPe+dYcY8C$$ZUy9mJ|3DMF z>mba?#m=aIh>>CEH8;}79hKz&_TG~AZEG(6?{C6Z^GyBYk0go2y13oxb;^&x#{)Uq L5p5grL8<=&`?GV8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b51ecf755901c5f87d42a5ac6383f09cf93849e0 GIT binary patch literal 2580 zcmZvec{mhm8^*`J?;1koxN0f2g<2pi4E*@Z&v+`|9>@$O&EMG3C_lfzzJ zvxd4v-P$O^^eNjYC&n~aVoTJx94#c5%dl8gZ9+k=B<`k-+NqReUI(_tGS5SXv}G6l zW0ZZ`Rm(wwVtR?Ru{@$o|0=;GRN&cdVVs4CEJ_>kVs7$h7WdY)(Ods->$J_E*NWGR zskGwv>xHwElX-kkmM;7%BW;u5FKjd_;xT_-9Pr2VEmJE?+qUy6zfYrR2-SWHM|)39 z(7(F|Z?5DRHoG`xc*?_}m>m0L0fchz? zD6!QvwO4uu!@g=NyJ^qjzSptG5;c$iezklsRWHU~iVxf8x;6U(z1AmJ#rI7YKwP@s zs+{XAzw1t3dC2uCYMJsz3MpCJ+rD_tMa*$`&WTH6Kvc1+> z({1pokhkX8q6E`zrt{GwaQGX3RnUj17zCtJ^lNO{{&>3Zt8E&+U<;B+gFxDc259D^(kM!xJrMBTd%qO!TYymj+YOB2S zxM4SzfT^((ELg|Bu<`n4mI|B1x|THc8pjjton+iX2J2Rfo8`x~3m?Cx7!96I%eUx) zdC9mT(m_eH%+QBU3lIxe0TKNRT0rUGs{9UIo-l4wyuVZ6lpoJ#*R0+zPQDNGfojCh z?{|D>MkgqLhE!4!Bq5Dk<@p^ZH~68O>8?Z`qLo&*oQEM?#;#+9gJHs*_waMw^mjzT zktU0D1l-xKqmskz#i3h|ubkP^c(S!rpeN5s@d<(X6#N-K-$e0V)ZEa`ATh##gkf1} zaHVD)*&pwvglkHV1kzxQ?~TMXmV5F$+VW352$TKTC==zMC7Y3fsu5bpOrl+L(S6>X z28c-Qq5u$x&Vjld7zk$XMUl`ep7Wy9GkHMiFj{_xIqk`V1g-KTk;mIS(=*q&gpOJ` zFkfMPyDG3By5g7FBwj(_jYYzIg8M}uL@WM?Y~3-WHNuKqGAhXzlQnm`Ws@)(WkEbwIbj=tA5qJ<>9L1wy0pj8jh5_L<%CT|XH{jRB$jv&sR+iZykk zd0menD?JgT)yb7dS6V$_!~Wik zSI}ycDJ^sG+*nN0l`HDs9MJk*Du@Hhgf03IuM($1lX`m0sEoJog)JOz6r5b_tRG$Q zx&I_u(j`*j>Cv-DLa{s$#I_|>Q*;NnoM_`3n`kBS(4z#=n;71U3dAW*QSDA_m~qNVwI~DwVjax~v5b&mMRl zo~G%~HQ;OS(DZ#rXLak%-WN+H2#uLrm$G9sQ#{v|))$A)G z=_wfPc91?bNW1hM=G|)nLs>KahVr{#E+tLFS&=rz8OMhH`bB#NCAEAxNHT8V??HNU z_+FK*TOknbpc1#og;2~9k-EaYMM1c%K}_Pvs&I-=kZBJaX{?43^O?anxl}_dk9T@U z1IdWO+nU59o-e2xp!nHCz{njruqUDSMrFx`63xUUX!l3IXvvq5q=BLPBVEygZz0$| z;l&W)+M5Km<{^_U3F|e())=LKLeBJd*Eb8D+AVOTU-{W7zu6Ncfp6r`94Zma57Wvi zVEzMi05b|Cdaj(~&h$)~b~ik+l68WUUA+3>2$+O2JV~&tieTunq(8n9hd}HdBG>Xdf~OQ9$s@z^)Kr#l zVXtrWv81EeGoVq>TFxCoB<#~n9(ZpLg^;mcX@V?xD2*XPtlf6L|Af~ECryp?caLo< z*9A9h#7RYb`;vW{F$-J{Q5|y9vR`8iWcxF0qq6xf8BNd!D;1y>d6%8&mZt9;5CVdk ze%fu7;oO6P?2(}6Q}Z#?iy zQ2mkeLDwr0T$javy!&D<6ZsPX9^vPY9Yr@}o~P41U7yMa?w^=cD9Sp15z}=50AgrICBg2dTv}+(H$=ZfD06-aj-^Yr;qW={h#y69>C;d8BXbY{rV%AK4?G zwew0e-nlSrc{?yi!Yl5^FeB^qiAjTP_>T%?W=5T&%c{u4vU$wCkzlkG5VC@#Z;|^2 zgih`Q>17{>;gyx?!gATCK+?pdkJ(nLxx%e-&4)befY9Y9*O zBk?X-W9RobdRb*69XSYjS-DvxDv46slDUzr82d6{qokY-9`TVoSgK*^dcrCW7RBMg z!oO~gydfnTA_2yLgrg;uq`)tuF~L^p|5Rt1CV23UFL}85RZqGp)aG2OA144p + enable_old_notifications + notifications_compact_view + 0,1,2 + scale_to_square_image_in_notifications + + prev + next + rewind + forward + smart_rewind_prev + smart_forward_next + play_pause_buffering + play_pause + repeat + shuffle + close + n/a + + notification_slot_0_key + @string/notification_slot_smart_rewind_prev_key + + Rewind / Previous + Previous + Rewind + + + @string/notification_slot_smart_rewind_prev_key + @string/notification_slot_prev_key + @string/notification_slot_rewind_key + + + notification_slot_1_key + @string/notification_slot_play_pause_buffering_key + + Play / Pause / Buffering + Play / Pause + Rewind + + + @string/notification_slot_play_pause_buffering_key + @string/notification_slot_play_pause_key + @string/notification_slot_rewind_key + + + + notification_slot_2_key + @string/notification_slot_smart_forward_next_key + + Forward / Next + Forward + Next + Play / Pause / Buffering + Play / Pause + + + @string/notification_slot_smart_forward_next_key + @string/notification_slot_forward_key + @string/notification_slot_next_key + @string/notification_slot_play_pause_buffering_key + @string/notification_slot_play_pause_key + + + notification_slot_3_key + @string/notification_slot_repeat_key + + Repeat + Shuffle + Previous + Forward + Forward / Next + Rewind + Rewind / Previous + Close + N/A + + + @string/notification_slot_repeat_key + @string/notification_slot_shuffle_key + @string/notification_slot_prev_key + @string/notification_slot_forward_key + @string/notification_slot_smart_forward_next_key + @string/notification_slot_rewind_key + @string/notification_slot_smart_rewind_prev_key + @string/notification_slot_close_key + @string/notification_slot_n_a_key + + + notification_slot_4_key + @string/notification_slot_close_key + + Close + Repeat + Shuffle + Next + Forward + Forward / Next + N/A + + + @string/notification_slot_close_key + @string/notification_slot_repeat_key + @string/notification_slot_shuffle_key + @string/notification_slot_next_key + @string/notification_slot_forward_key + @string/notification_slot_smart_forward_next_key + @string/notification_slot_n_a_key + video_mp4 video_webm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3625e67f4..1d1c67364 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,9 +57,18 @@ Install missing Kore app? org.xbmc.kore Show \"Play with Kodi\" option - Lock screen video thumbnail Display an option to play a video via Kodi media center - A video thumbnail is shown on the lock screen when using the background player + Enable old notifications + This enables the old \"Custom RemoteViews\" notifications. If disabled modern MediaStyle notifications will be used. + Scale image to 1:1 aspect ratio + This will scale the notification image from 16:9 to 1:1 aspect ratio + Notification slot 0 + Notification slot 1 + Notification slot 2 + Notification slot 3 + Notification slot 4 + Notification compact view + Notification slots to show in compact view Audio Default audio format Default video format @@ -129,6 +138,7 @@ Other Debug Updates + Notifications Playing in background Playing in popup mode Queued on background player diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml index 31be267af..a77663843 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -1,38 +1,110 @@ - + android:title="@string/theme_title" + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> + android:title="@string/list_view_mode" + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index d1757919b..0ff43ce90 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -81,13 +81,6 @@ android:summary="@string/show_play_with_kodi_summary" android:title="@string/show_play_with_kodi_title"/> - - Date: Thu, 14 May 2020 17:05:09 +0200 Subject: [PATCH 02/30] Use vector drawables for close and replay --- .../schabi/newpipe/player/NotificationUtil.java | 16 ++++++++-------- .../res/drawable-hdpi/ic_close_white_24dp.png | Bin 221 -> 0 bytes .../res/drawable-hdpi/ic_close_white_32dp.png | Bin 257 -> 0 bytes .../res/drawable-hdpi/ic_replay_white_24dp.png | Bin 675 -> 0 bytes .../res/drawable-hdpi/ic_replay_white_32dp.png | Bin 804 -> 0 bytes .../res/drawable-mdpi/ic_close_white_24dp.png | Bin 175 -> 0 bytes .../res/drawable-mdpi/ic_close_white_32dp.png | Bin 542 -> 0 bytes .../res/drawable-mdpi/ic_replay_white_24dp.png | Bin 457 -> 0 bytes .../res/drawable-mdpi/ic_replay_white_32dp.png | Bin 583 -> 0 bytes .../res/drawable-xhdpi/ic_close_white_24dp.png | Bin 257 -> 0 bytes .../res/drawable-xhdpi/ic_close_white_32dp.png | Bin 666 -> 0 bytes .../res/drawable-xhdpi/ic_replay_white_24dp.png | Bin 908 -> 0 bytes .../res/drawable-xhdpi/ic_replay_white_32dp.png | Bin 1168 -> 0 bytes .../res/drawable-xxhdpi/ic_close_white_24dp.png | Bin 347 -> 0 bytes .../res/drawable-xxhdpi/ic_close_white_32dp.png | Bin 436 -> 0 bytes .../drawable-xxhdpi/ic_replay_white_24dp.png | Bin 1390 -> 0 bytes .../drawable-xxhdpi/ic_replay_white_32dp.png | Bin 908 -> 0 bytes .../drawable-xxxhdpi/ic_close_white_24dp.png | Bin 436 -> 0 bytes .../drawable-xxxhdpi/ic_close_white_32dp.png | Bin 1852 -> 0 bytes .../drawable-xxxhdpi/ic_replay_white_24dp.png | Bin 1885 -> 0 bytes .../drawable-xxxhdpi/ic_replay_white_32dp.png | Bin 2580 -> 0 bytes .../main/res/drawable/ic_close_white_24dp.xml | 9 +++++++++ .../main/res/drawable/ic_replay_white_24dp.xml | 9 +++++++++ .../layout-large-land/activity_main_player.xml | 10 +++++----- .../main/res/layout/activity_main_player.xml | 12 ++++++------ .../layout/player_background_notification.xml | 12 ++++++------ .../player_background_notification_expanded.xml | 6 +++--- .../res/layout/player_popup_close_overlay.xml | 9 ++++----- .../res/layout/player_popup_notification.xml | 6 +++--- .../main/res/layout/toolbar_search_layout.xml | 4 ++-- 30 files changed, 55 insertions(+), 38 deletions(-) delete mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_24dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_32dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_replay_white_24dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_replay_white_32dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_32dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_replay_white_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_replay_white_32dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_32dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_replay_white_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_replay_white_32dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_32dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_replay_white_32dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_32dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_replay_white_32dp.png create mode 100644 app/src/main/res/drawable/ic_close_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_replay_white_24dp.xml diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 68648a8ad..62f25810b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -356,7 +356,7 @@ public final class NotificationUtil { } break; case "close": - builder.addAction(R.drawable.ic_close_white_32dp, "Close", + builder.addAction(R.drawable.ic_close_white_24dp, "Close", PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, new Intent(BackgroundPlayer.ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT)); @@ -450,7 +450,7 @@ public final class NotificationUtil { 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, + new NotificationCompat.Action(R.drawable.ic_replay_white_24dp, "Completed", PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), @@ -486,7 +486,7 @@ public final class NotificationUtil { PendingIntent.FLAG_UPDATE_CURRENT))); } else if (basePlayerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { notificationBuilder.mActions.set(slotNumber, - new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + new NotificationCompat.Action(R.drawable.ic_replay_white_24dp, "Completed", PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, new Intent(BackgroundPlayer.ACTION_PLAY_PAUSE), @@ -611,7 +611,7 @@ public final class NotificationUtil { break; case "close": notificationBuilder.mActions.set(slotNumber, - new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + new NotificationCompat.Action(R.drawable.ic_close_white_24dp, "Close", PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, new Intent(BackgroundPlayer.ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT))); @@ -840,7 +840,7 @@ public final class NotificationUtil { } break; case "close": - builder.addAction(R.drawable.ic_close_white_32dp, "Close", + builder.addAction(R.drawable.ic_close_white_24dp, "Close", PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, new Intent(PopupVideoPlayer.ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT)); @@ -927,7 +927,7 @@ public final class NotificationUtil { 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, + new NotificationCompat.Action(R.drawable.ic_replay_white_24dp, "Completed", PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), @@ -963,7 +963,7 @@ public final class NotificationUtil { PendingIntent.FLAG_UPDATE_CURRENT))); } else if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { notificationBuilder.mActions.set(slotNumber, - new NotificationCompat.Action(R.drawable.ic_replay_white_32dp, + new NotificationCompat.Action(R.drawable.ic_replay_white_24dp, "Completed", PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, new Intent(PopupVideoPlayer.ACTION_PLAY_PAUSE), @@ -1090,7 +1090,7 @@ public final class NotificationUtil { break; case "close": notificationBuilder.mActions.set(slotNumber, - new NotificationCompat.Action(R.drawable.ic_close_white_32dp, "Close", + new NotificationCompat.Action(R.drawable.ic_close_white_24dp, "Close", PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, new Intent(PopupVideoPlayer.ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT))); diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png deleted file mode 100644 index ceb1a1eebf2b2cc9a008f42010e144f4dab968de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmV<303!d1P)og+*{ z>6z1@lfD*AYSPav7 zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| diff --git a/app/src/main/res/drawable-hdpi/ic_replay_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_replay_white_24dp.png deleted file mode 100644 index fcddcf02ddb58ee1680889e7315757d76419b4b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 675 zcmV;U0$lxxP)i^4O#T$_9m82|5VvpF*wP4OP zM$UZi@^>7mIp;Yu=T~<=&tBi0Ip_J;@t6l6eCkO56W1~dg3&=h1q zQ;-2oK?XDh8PF7DKvR$bO+kjAeDJ~pr|dFMzEwenMqRT?w%I|3a4PK3rz1$XY>?9M z$o%dYv=53msmx64U;8dHjIzKUk9_ChB>7>S;1G`>C)ufEh%>(Om`-|fb*SR^#z4}` zk1=8_ANv5(l+5L85*Xb37G?llB;BT5>A(ui94pbtcshM*TjCYGR_ z-6MNIjB5B%pCT4Cy!N|@y;y<{>^_Y1h{sr_j%UPd_@F#~6P7u_$u$?{J9LdWv<^B( zTt-`dtRg&yta3*15X;emvQ!XfRzVjCmrT?J+75nB>W0d9%nhW=(93#VAeC5$p_bT;=ksFy7&G{R+*d}}W7vaE1LouAaX zWSLB>FM)fQW1AE1DN&Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=Y>Ua!!*MzB~-b zcPe>dSN|c`ndF7t{f0bqlNT=1U&ygAdEsLHg#6|vZ+@fC=nZ;~4$-zlEDw?>lY9~1 z(FxjhxXN6x?V3yIA$m_Ej?iX&wQH!j&RfW$hU_?Ow@~q-My#W2=pK5EKA=3#(4NC~ z3l$eO!bMWJw1)Q4sr&(IM8DpI-810YLNDS%)2KW3XH3|Y3+@O#jSEh5D)|Lordfoj9<&It1B9TGq4d+uaz%JeY(|X>Kb)ARWN7 z=t*taT^q$h9~C&mvEB6{*e?1Nhy91Pz_r&A#Xv6=FoU+!&7RB%sILbhI*e3qgziPT zab2W22)c=M+zZj0ICpo_Jt%zg;vLdwm*yex4${FpL}Nnx`GvES${-Ecu+NK6=!T27 z0~PF{yhaF3vDy;fN7|TeSo*DN^5uPv5Z!%Np(4GByzW#Lb(?mdd(AdV#S~tjs60{lf39H8_eGT0000& diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png deleted file mode 100644 index af7f8288da6854204dcc4e6678b9053cd72032c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+iGEW!B5R21KC-3Avpdi4K$<+1X zay^SH!@6{thMdKZi+L1hS^BMZUAdKCT}(HRw{NeWg~AlBHtr3jj>2B5kK^|J))MUC zlz7ydwCsb8xn}W?^r?}5wtsjdmvX{3=JbT!J9sKPrYmk={m|7^>A|Z*7j^D$`>}ry Zqi^Z9GQN4ct^!@a;OXk;vd$@?2>?I&LQeny 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 deleted file mode 100644 index 20b61eaeec893baf305a701f78fc9c18eee0217d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 542 zcmV+(0^$9MP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0kcU&K~z{r?UqYU z0zni-(V?J2*o-c~&pJf+060|H7;Zoqh$HzVO3FzR68}UcLM3 z)Ag!L1qu{M!fZA>fJd4-emjh9!LfZi(y}={aMTvo;Shm{vJab7_dH|;*vs$=KH5hUmqyJ)vH?d4 zHeug_11Q0jZ-PEe_yUeR_5o_bsgEue5`tkTCQy?o+Z-mU;1iE|f(@JV!vPdD1kl#D zY?fVT9#;NLaZWj(;8uWO6>61XC%W(!wqehG0|(E1ZBqeZ?SxjV;2-dnfNzxV@E$g3 z$9|++1A*EK48K8pN^xUzChWTW8(^j3p7)@RU}~zaBS_2>xq$fcJy*bMZ9QezeX)5W z7Z6Om=hPST#0&h9CvpMJklSPLx$1B^Po#P~5`k6)p~c)Dd(YJeHHpQyqeXSIRE%>t z^RWe$+Y|4(Y>d%vg|}k_rIGzH!hiWWPLFTt0oJ-PWqJo#>PDsS z`vtg-Yj!6pzPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0o+MMK~z{r?Up@D z!%!548!hOdv<0i9j_RP`p!f%L7VYNl>gJ$>n}a{YNjEzRx;O|fej#-eoI6y74t@~d z_vTGWNMq9W8W8%x!%fb)=bUS;iMfA`>$=Oj%pMMhue!|^e>ij%%8uZ0qHs1~UT8Q` z>hHm<$Z)X+nH$ws`)EMB;Xyv(Ijq0|mD&Lf(;OhkHJnws9ndiD5SwrhUche##5-J3 zxgF3jatNgixSfV)CgIV9OG?`T4MT@exCKHEJTyss?9ni3K2!%p043C%>`%2jZ zP0b;68v(fztV68~#%@@`7899Q#cuHrnjcdb-&<(QmJ8$Y6*KOlM)ncm{N}#EGd-;_o~KcU8(e-h*nLx z#|@lRIu`EHUY}E_Y=@w*2p`x*@cF+B$F-#eViSTtVCW^hrT3_XEl~ V_uUT1{e}Pl002ovPDHLkV1lm9{aOG3 diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png deleted file mode 100644 index b7c7ffd0e795ba76ed3a062566c9016448795f7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmV+c0sj7pP) zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| 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 deleted file mode 100644 index cc13347341479e29fc567f6af466875e91e4048c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 666 zcmV;L0%iS)P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0xwBKK~#8N?V4du z0x=MU(H~w16Yv6_0Zy%_&_u7GzwmozAY|O_E^D{6?0m^fgmydAz6TQ6RS*P05ClOG z1VJ!?!{P9Vp6y{VR1>Z3VRpSaWP{-OhVxDoYYH$L4lXpYtA?o0J4SXJc(S@1e;_ zNzijP)`c=ejLr> zyoWW1l2Go)QHrgRev)sbHb5XocB<}CqX@IY`7d0)h#kQ zglpd-vq5(@Tpo%q^Cqbv2!bF8f*=Tjc3G`{0C;dH`T`V^N&o-=07*qoM6N<$f@YH? A5dZ)H diff --git a/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp.png deleted file mode 100644 index 1573fb111b535e5e0df9371ff7f804ce2c40a0fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 908 zcmV;719SX|P)7!(!0)U7tJ0c*i|=W=`GD{Rhrh*S+VSbLJ}_yb|Y} z2=nyPP+9@g9^_WikSR{kYsy-hy zEku#&*XNV6oCC6@Y0~GT-ej9>Czv`dtH=_DbXwRliC~qaN~fj83SwBz>9x4qN(Rvr ze!Z60m#~`DYlFi_98nBIuPvr=tUC1CrC;Y$3W(^FgzQC`p+k!}pOw6jBq=iPfkh0TWmS)dUz^Bf7(L-mV&-4}J$XOF7sBlc6Scm>hhDc(^Uj{LXd-%JWIiC2AM`rMs#_tFGOcEm> zQ6{J?t)HM6bg;rDU%4Peu#|sCR!~qE~FTC?knj22pV}>5;)E~ds^l}5-05`x5 ia0A=`H^2>0aQz3T@7w=K#~w`p0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1SLsCK~#8N?V8_< z6=fL5_efgpvvr?4ngx z5WHCxL7{9>Q7{wwwK@B+;?YU%&MqDmkEV2J_AtG8Lx*Pv)Z13Pp_lp}YITY?^m2bgo!;UNx%4m8 zm|DCcxBi6sIu>upwf~^D%;F8X_ZQTat9V00YNI7v8q{PzZ;zri(Sd9ZqC4nMbP=6F z=UT1SB(}G(;!&=5K7(86JG2o!q3OehM9V5Z$rZE*J*`lsGDkLKmZC4w1)8uSIua@* zSkw~pZU6#6O} z;O8^Bh6xF#HbE|#;Mzi8M1x!&pcQfr6B6_`K`-Y5cWc7IXrRk+xrGZ!I!(|^xnamW z^jkF8W|DhRl#kUQ#&>@Gp!2cokdv(!K8W|)*g{T+ zv|SDa`vCc^owhnx;El0~W7r7h2MdcyJp4ucCmaPL{>~V6<{Zy&w*Wjq^bk=dv8Payw zAm+=MweemX$7FL+v;n@~**3=~@n%=qjePkRx)&$}UAG$$dqR`sZ`8GQdSVTtI~F*> zc_gojAi%CDl*{*WsT^V*ibFc=luLCGty(ibwV9xh7w~t>@>3{a<2my!CO^fcq*6H`6 zqTezOHV66?*?OOk8^~_dx(|VEbvErIY1kvJRdO#3V^?dAG%WU8a<2yx*oN#K@5C=? zGn%VmshE#^ZA>RD=2i`xF{PY0Br<~e7G+(9?;=|XK8q&MqcuTW99@Q9M{l64TkLyi zJ3o1UjCTWoCagmCf1awjj8-aaSdiF!v>*M4%EspW8qHU@a3RUH=maVYo9j(Y-#d`B zHR2m&_Xn6?(TivvGVGtC*OC2G^fWqw#<vMkH8EXyh{JTRaptiwcq@D(P;=ehLbl?EMKm*m7(@7_siS}}k zNFtnck{HL4mc!ypcyUoqJV~4rM^fS3C#iAnkyJU3biCmDy`VZLOv=LXld^HVq32ETvhYyB39D^lU%DgQgv&#U%8l^-CA%qY@2qA=!B5=cm zUgehujQG*l{{`@rPr!f|fEk^>KI9WteE@ilV6HEl&_rJ@p_zU*;T}RirIc{5NocNLm*7JGdV(AM zYYDFOvk5~8{Z*t_>U=!XvoehUSEh=adIga45oe)B9kGgT}7UT3Cirmr(oHPv^YQ1-p=Hlh5u;xggf zX{&yw+El-OrrKQJRl@b7x{HLmNkj95`a#LLnW{Veb2C+!`ppt#r)=g4@?c8RY{yJj~W@W|h^mU4q`i)2y~Rw@J`jIh$1%|In!~{Tb{nMqaxlgb+dq eA%qY@zJ@mriVM?qfwL0;0000BgE0Bosd;&l!5{GB}Nw&aWDv$#(+==h%zxz{0{`dg+v3oNsZEs1O#b7TU=z3 zN+U>6X{oItF;)nPJ2M7_WMc9%Um4!^-ghT8^Ukw#zTbcF!Mpd}bI-XHnCAtLxJ@r7 z*+WWFkh~r@MJBk;QR;*R$?I{j$^*{OAS_5;j{}dp93n0(NM4VF=X6pdEJ$9DgITg9 zR8TMzWb_kr@e}pBNC)$pcHQP(8I2%dRM20TlCA~t5IK}Qkj#7O)Q^3r9eOSQH4a|6K{m=!i&(jnw*l3S} zAo;|Y#mrR|c`AQ73Aj>W`=NK1ad*g$rgcHjacN zsE$?43`9cEp#0VI1I#Q^C$1+=0W+iFH7}gO##wPe=dsZho}dOi%*>M#)$=KDF|$HC z%=VYN*tj4n=n^*Wgxi952pdam7t`|<%h)&=uAn&2u`wd{|1*k>Nn)W2a<^>@q#HZO z!*3+7VHO(;>=dXW7O*kRCla0ZS?o+w8>*fZ|6u2s%rHxW3G57$2u;t&j9}+4Hb_nT zWC$WhETqMGkRZO19H<*2h`taM#pox9o3bOB^}HsC9El2lT!REL$7j+rtL+3cT*2i! z#R$QC8-=m(9KlS|RH2RiIl)|u#=`OrwtD`Z#efJ`%0Sgyv2>j;V3?^)IpJQ zC~$)oVryJ;_A*F;awzhBRG;YTz@uDdxXb~P?psnaT;V0<;<@!m_9(Wo#9CP45&fK^ zm0dKDWCIB{)4(oTIZZ#0Sz#>{I2`q70$a!<#VdblmKEeqBTtav5BJ6{FUU`l0m9~* z`?oM!L%nU}35z-RnM+)dA!%+A1~=V*R#Jl8+0QtYYudtsG%831sUQ`k wf>e+SQb8(6qk>eB3Q|ETNCl}N6{IobUk;_uiK)$-$N&HU07*qoM6N<$g8NE^b^rhX 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 deleted file mode 100644 index 1573fb111b535e5e0df9371ff7f804ce2c40a0fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 908 zcmV;719SX|P)7!(!0)U7tJ0c*i|=W=`GD{Rhrh*S+VSbLJ}_yb|Y} z2=nyPP+9@g9^_WikSR{kYsy-hy zEku#&*XNV6oCC6@Y0~GT-ej9>Czv`dtH=_DbXwRliC~qaN~fj83SwBz>9x4qN(Rvr ze!Z60m#~`DYlFi_98nBIuPvr=tUC1CrC;Y$3W(^FgzQC`p+k!}pOw6jBq=iPfkh0TWmS)dUz^Bf7(L-mV&-4}J$XOF7sBlc6Scm>hhDc(^Uj{LXd-%JWIiC2AM`rMs#_tFGOcEm> zQ6{J?t)HM6bg;rDU%4Peu#|sCR!~qE~FTC?knj22pV}>5;)E~ds^l}5-05`x5 ia0A=`H^2>0aQz3T@7w=K#~w`p000032ETvhYyB39D^lU%DgQgv&#U%8l^-CA%qY@2qA=!B5=cm zUgehujQG*l{{`@rPr!f|fEk^>KI9WteE@ilV6HEl&_rJ@p_zU*;T}RirIc{5NocNLm*7JGdV(AM zYYDFOvk5~8{Z*t_>U=!XvoehUSEh=adIga45oe)B9kGgT}7UT3Cirmr(oHPv^YQ1-p=Hlh5u;xggf zX{&yw+El-OrrKQJRl@b7x{HLmNkj95`a#LLnW{Veb2C+!`ppt#r)=g4@?c8RY{yJj~W@W|h^mU4q`i)2y~Rw@J`jIh$1%|In!~{Tb{nMqaxlgb+dq eA%qY@zJ@mriVM?qfwL0;0000Rol24V(Ul{cf{Ft-O^TClm@M})Kx4+>gFppf_Al9 z#ZupGE6zrcR@~QaqeT$ZRuoaI`|%I#4>R+;&olGR%=^sqOcse~F9%hJfcj#!iW);ZTYO z>T=`0X?%9WrcrsC_4A<7*sb<(ZqAXCz$&?zvtCH)DOTz*!{g)CUw2Q0|C}824PDMP z{$6;es_Q`y_y6;2&Fj*OPLzVPYLCjEjk4Z}J3<9+DeG=LyRliiHMy^7kYa3n-Q4^r z=Je=Wh=B)$5psZ;8FFDrR_u@>qb1o1CW4Lrhu0eYPa_i2OYrb(DZ%v))2sppo^;_8 zwb0{z5aLC)COh6l@b#o`w)J1gE$qmZkTWeSW5#}~Xiul~Dxm4=GI?L5oo$KYsh#st zwF=q4KstT`?fKPzz&H~P!{@!{OvE10md89lIPwS=WZaj)>KZoRD~V{@46XE6=K{7Z z#TJUPzMa;tA4a)CC9urOT$T|k$R(Y8;K}A59pxC00f8W^h%BO>TYr4?7hHASPMEOW+xq zFH~-%I>LxXKFc)6rpyej6+LE67n2X3+)vBbZ6Xq*vPwnFK{koOF-)46tJkHbt`_CC z4~~`Y+2)BOl531IJ#YOAt5e85f)%tuz@ip4c$?e*j<+^{78Qn(p*6SlH~cb_wYzPx5QLsf0a_{(8oi)_v$2D9_qHUS znS#W~8YucFs*MdqD!%#701|5{e;~DD{uS=EI3H9Ap~GU1M3Z)YNZ>Qi;Aq{#ZVJO8 zkZ_a8!%)rgo*_inYCPk%r?6Rz^*@$I=u1TC@(p8%JXJG5{>i&cGGi$W`=ovkX|n{5 zNG@FM-Se{a0r>_AZlQ{kc z;$*=H;?31f0JTDAa&-*Rz&Xkz)X+*{FX!Np=|rriNJQduu(;8=`;?G7n!pV{ro*%j z_jT!190rJ5o*7(4HhNQ2yAMaBe-ptSN=qc-eHQ7-`1QRE+kgk0Q#7_{655_0V@?mq2q203()6r6+x2FiJqPX|2^x7}2$3tNsI zn?0;zlfo9k4wg=6!``}M%V9A>27v)~tEs` zOZ=Us)a$C+p_WVFS6eBx_1}#`02qtJ`c5=Krxav*hS1(&vwD7grZ4tZ2tFCF>Aj|m8d`?t9S6@!6^#L z@*k^9m(K|S&7>;w1mNb_%*kx=#$CN0sd;;|kmG+5c6jPe+dYcY8C$$ZUy9mJ|3DMF z>mba?#m=aIh>>CEH8;}79hKz&_TG~AZEG(6?{C6Z^GyBYk0go2y13oxb;^&x#{)Uq L5p5grL8<=&`?GV8 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp.png deleted file mode 100644 index 04cbde9af1926410c77e666492a8c5407d938f2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1885 zcmV-j2cr0iP)G0000LhNkl?*|(>Q-}x=BZLQ4&YpE&mJ&MS+o(x;tb+l&fx65xW;&^oP6*;;qZ`277j0&B;g2y13QyVxcB2x=EDkM`q92JwP7LF>&R0l^@ zWU7IqT37j=K`wEM_jsFaJi{v5DanN4NXjtpvz;}}lo=0>dN|9gbkZzyEgbbU#4aA8 zQRXK&((omlnJeIN0diU~(n^z$bb4~{H3$n7c`99eROtxQ(2;K-J9 ztWdGw$ePz^QIX)tno}%Rk>JRh5uQ|$;K-g=Xi#zB$etcrRU9~~)5H{JvygjP&5P`! zk0Gk{A=6bHII5uQP@;q9+0P)AJIgE;2abwqTr_YWuQNbpF3_&xz)>Lu(9CLjC==fW zW~n%ER78=mfZdD{*IA~kIB--zkujGyxI#Srw5m98#HLv2;281r(4gYL5tCw~#8Z4l z99=3B9I>bZ+{!z|u}(#TBL-E7XBj1`5f-UPa73nxaTn)^>JwU2Bsd~c6_`pNQEgYT z;D|(3p_Ri#Q|2KR3yug>B^v1_n$Ngd#eyS9Rii{V(fmV2gCjsSKqH5V;xfNi(cth> zP0&goQ5;b5;P6n5(8hV9SgGQ{F)q~%cQZ=Z2^A5Jaj1rPo+utw5#hKY)fD>(>sK-1 zxB=A^Q}~*&2UScsuA>^`X~KF{R5*S^HAad55K?BIiVDXyRC9DNM#xJlE*!t21h9jU zFPNm_!to0wfVqqj^00~w$Ip}qx(Rtp#fIZYN(2ij<2OK)iVepPln{CeS*fDKffB+R zLb_ydN(e0s;&(s}pO7bHl0ySS_;tx7i39i@lSvXU;5R6fB<>_+woGy; zF^pfQOp@rwZ@o;C=*Dl0Op@4$-(Hy{u@=80GD%`7ekWy;Lqo1 zGD)HtzcHnatN1l3$7sfHOljkL{H7?!Xv1$rX=4z-*~&5I;P;2zgmqK_?+S${7w4vO-xwGeh{5S)d$Y5oP=aXi|2ti;#aRN7zHiPGt#?5Hd!) za)S9>C8SeXLL*-i@`iGPorGLslCp)(gj`{RC89@{O zCW=w!Df`$;G`lG&11PbFXf`Pin9Mn%*-fKTMiYC8=2Kdf53C@n!?Y?z+`_+zrpyD% z4_+gxKH8KLrttw$btzA1;WSa5=WeBdd-$BFKBigu!eT~ zNgVr_qB>zJ?-Ivqp|mlQH7yW3uv%>6|65F}hix8e$Q97$dI#F-`f$EG`gVnO@e=qAJnCIu24M zzVpn~#6UY2sLUWcxSs}9fJrRlO$MpVdFE*1U>0Yo+zyvz!kWGch&c!DmDF+?1L9Az7iGef3Um`x|^ z*}`6qaFUA*Fv1vPjPNZNImr?BvW4|@a+{1X#u#IaF~%5Uj4{R-V~jDz7-NhvCN=*D X>#M$jzV5xv00000NkvXXu0mjfXY*@a 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 deleted file mode 100644 index b51ecf755901c5f87d42a5ac6383f09cf93849e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2580 zcmZvec{mhm8^*`J?;1koxN0f2g<2pi4E*@Z&v+`|9>@$O&EMG3C_lfzzJ zvxd4v-P$O^^eNjYC&n~aVoTJx94#c5%dl8gZ9+k=B<`k-+NqReUI(_tGS5SXv}G6l zW0ZZ`Rm(wwVtR?Ru{@$o|0=;GRN&cdVVs4CEJ_>kVs7$h7WdY)(Ods->$J_E*NWGR zskGwv>xHwElX-kkmM;7%BW;u5FKjd_;xT_-9Pr2VEmJE?+qUy6zfYrR2-SWHM|)39 z(7(F|Z?5DRHoG`xc*?_}m>m0L0fchz? zD6!QvwO4uu!@g=NyJ^qjzSptG5;c$iezklsRWHU~iVxf8x;6U(z1AmJ#rI7YKwP@s zs+{XAzw1t3dC2uCYMJsz3MpCJ+rD_tMa*$`&WTH6Kvc1+> z({1pokhkX8q6E`zrt{GwaQGX3RnUj17zCtJ^lNO{{&>3Zt8E&+U<;B+gFxDc259D^(kM!xJrMBTd%qO!TYymj+YOB2S zxM4SzfT^((ELg|Bu<`n4mI|B1x|THc8pjjton+iX2J2Rfo8`x~3m?Cx7!96I%eUx) zdC9mT(m_eH%+QBU3lIxe0TKNRT0rUGs{9UIo-l4wyuVZ6lpoJ#*R0+zPQDNGfojCh z?{|D>MkgqLhE!4!Bq5Dk<@p^ZH~68O>8?Z`qLo&*oQEM?#;#+9gJHs*_waMw^mjzT zktU0D1l-xKqmskz#i3h|ubkP^c(S!rpeN5s@d<(X6#N-K-$e0V)ZEa`ATh##gkf1} zaHVD)*&pwvglkHV1kzxQ?~TMXmV5F$+VW352$TKTC==zMC7Y3fsu5bpOrl+L(S6>X z28c-Qq5u$x&Vjld7zk$XMUl`ep7Wy9GkHMiFj{_xIqk`V1g-KTk;mIS(=*q&gpOJ` zFkfMPyDG3By5g7FBwj(_jYYzIg8M}uL@WM?Y~3-WHNuKqGAhXzlQnm`Ws@)(WkEbwIbj=tA5qJ<>9L1wy0pj8jh5_L<%CT|XH{jRB$jv&sR+iZykk zd0menD?JgT)yb7dS6V$_!~Wik zSI}ycDJ^sG+*nN0l`HDs9MJk*Du@Hhgf03IuM($1lX`m0sEoJog)JOz6r5b_tRG$Q zx&I_u(j`*j>Cv-DLa{s$#I_|>Q*;NnoM_`3n`kBS(4z#=n;71U3dAW*QSDA_m~qNVwI~DwVjax~v5b&mMRl zo~G%~HQ;OS(DZ#rXLak%-WN+H2#uLrm$G9sQ#{v|))$A)G z=_wfPc91?bNW1hM=G|)nLs>KahVr{#E+tLFS&=rz8OMhH`bB#NCAEAxNHT8V??HNU z_+FK*TOknbpc1#og;2~9k-EaYMM1c%K}_Pvs&I-=kZBJaX{?43^O?anxl}_dk9T@U z1IdWO+nU59o-e2xp!nHCz{njruqUDSMrFx`63xUUX!l3IXvvq5q=BLPBVEygZz0$| z;l&W)+M5Km<{^_U3F|e())=LKLeBJd*Eb8D+AVOTU-{W7zu6Ncfp6r`94Zma57Wvi zVEzMi05b|Cdaj(~&h$)~b~ik+l68WUUA+3>2$+O2JV~&tieTunq(8n9hd}HdBG>Xdf~OQ9$s@z^)Kr#l zVXtrWv81EeGoVq>TFxCoB<#~n9(ZpLg^;mcX@V?xD2*XPtlf6L|Af~ECryp?caLo< z*9A9h#7RYb`;vW{F$-J{Q5|y9vR`8iWcxF0qq6xf8BNd!D;1y>d6%8&mZt9;5CVdk ze%fu7;oO6P?2(}6Q}Z#?iy zQ2mkeLDwr0T$javy!&D<6ZsPX9^vPY9Yr@}o~P41U7yMa?w^=cD9Sp15z}=50AgrICBg2dTv}+(H$=ZfD06-aj-^Yr;qW={h#y69>C;d8BXbY{rV%AK4?G zwew0e-nlSrc{?yi!Yl5^FeB^qiAjTP_>T%?W=5T&%c{u4vU$wCkzlkG5VC@#Z;|^2 zgih`Q>17{>;gyx?!gATCK+?pdkJ(nLxx%e-&4)befY9Y9*O zBk?X-W9RobdRb*69XSYjS-DvxDv46slDUzr82d6{qokY-9`TVoSgK*^dcrCW7RBMg z!oO~gydfnTA_2yLgrg;uq`)tuF~L^p|5Rt1CV23UFL}85RZqGp)aG2OA + + diff --git a/app/src/main/res/drawable/ic_replay_white_24dp.xml b/app/src/main/res/drawable/ic_replay_white_24dp.xml new file mode 100644 index 000000000..8e84c195b --- /dev/null +++ b/app/src/main/res/drawable/ic_replay_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml index 16dcff639..8f683ccdf 100644 --- a/app/src/main/res/layout-large-land/activity_main_player.xml +++ b/app/src/main/res/layout-large-land/activity_main_player.xml @@ -65,18 +65,18 @@ android:id="@+id/playQueueClose" android:layout_width="50dp" android:layout_height="50dp" - android:layout_centerVertical="true" - android:layout_alignParentRight="true" android:layout_alignParentEnd="true" - android:layout_marginRight="40dp" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" android:layout_marginEnd="40dp" - android:padding="10dp" + android:layout_marginRight="40dp" + android:background="?android:selectableItemBackground" android:clickable="true" android:focusable="true" + android:padding="10dp" android:scaleType="fitXY" android:tint="?attr/colorAccent" app:srcCompat="@drawable/ic_close_white_24dp" - android:background="?android:selectableItemBackground" tools:ignore="ContentDescription"/> + tools:ignore="ContentDescription" /> + android:layout_height="64dp" + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto"> + app:srcCompat="@drawable/ic_close_white_24dp" + tools:ignore="ContentDescription,RtlHardcoded" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/player_background_notification_expanded.xml b/app/src/main/res/layout/player_background_notification_expanded.xml index e5a9b632b..80bc22627 100644 --- a/app/src/main/res/layout/player_background_notification_expanded.xml +++ b/app/src/main/res/layout/player_background_notification_expanded.xml @@ -29,8 +29,8 @@ android:focusable="true" android:padding="8dp" android:scaleType="fitCenter" - android:src="@drawable/ic_close_white_24dp" - tools:ignore="ContentDescription,RtlHardcoded"/> + app:srcCompat="@drawable/ic_close_white_24dp" + tools:ignore="ContentDescription,RtlHardcoded" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/player_popup_close_overlay.xml b/app/src/main/res/layout/player_popup_close_overlay.xml index cba06874e..e7607edaf 100644 --- a/app/src/main/res/layout/player_popup_close_overlay.xml +++ b/app/src/main/res/layout/player_popup_close_overlay.xml @@ -1,6 +1,5 @@ - @@ -11,8 +10,8 @@ android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="24dp" - app:srcCompat="@drawable/ic_close_white_24dp" app:backgroundTint="@color/light_youtube_primary_color" app:borderWidth="0dp" - app:fabSize="normal"/> - \ No newline at end of file + app:fabSize="normal" + app:srcCompat="@drawable/ic_close_white_24dp" /> + diff --git a/app/src/main/res/layout/player_popup_notification.xml b/app/src/main/res/layout/player_popup_notification.xml index e8b077ecc..1aa767c58 100644 --- a/app/src/main/res/layout/player_popup_notification.xml +++ b/app/src/main/res/layout/player_popup_notification.xml @@ -83,6 +83,6 @@ android:focusable="true" android:padding="5dp" android:scaleType="fitCenter" - android:src="@drawable/ic_close_white_24dp" - tools:ignore="ContentDescription,RtlHardcoded"/> - \ No newline at end of file + app:srcCompat="@drawable/ic_close_white_24dp" + tools:ignore="ContentDescription,RtlHardcoded" /> + diff --git a/app/src/main/res/layout/toolbar_search_layout.xml b/app/src/main/res/layout/toolbar_search_layout.xml index 9a7d56a6e..74647e2e0 100644 --- a/app/src/main/res/layout/toolbar_search_layout.xml +++ b/app/src/main/res/layout/toolbar_search_layout.xml @@ -47,7 +47,7 @@ android:contentDescription="@string/search" android:scaleType="fitCenter" app:srcCompat="?attr/ic_close" - tools:ignore="RtlHardcoded"/> + tools:ignore="RtlHardcoded" /> - \ No newline at end of file + From caf7c55069804dce58b4be8e9df5b8211ae5ac78 Mon Sep 17 00:00:00 2001 From: wb9688 Date: Fri, 15 May 2020 15:05:20 +0200 Subject: [PATCH 03/30] Log only in debug build --- .../newpipe/player/NotificationUtil.java | 16 +++++-- .../player/helper/MediaSessionManager.java | 48 ++++++++++--------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 62f25810b..d88a26b2f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -75,7 +75,9 @@ public final class NotificationUtil { if (areOldNotificationsEnabled) { notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); } else if (notificationBuilder == null || recreate) { - Log.d(TAG, "N_ recreatePopupPlayerNotification(true)"); + if (DEBUG) { + Log.d(TAG, "N_ recreatePopupPlayerNotification(true)"); + } notificationBuilder = createPopupPlayerNotification(context, mediaSessionCompatToken, playerImpl, sharedPreferences); } @@ -91,7 +93,9 @@ public final class NotificationUtil { if (areOldNotificationsEnabled) { notificationBuilder = createOldPopupPlayerNotification(context, playerImpl); } else if (notificationBuilder == null) { - Log.d(TAG, "N_ recreatePopupPlayerNotification()"); + if (DEBUG) { + Log.d(TAG, "N_ recreatePopupPlayerNotification()"); + } notificationBuilder = createPopupPlayerNotification(context, mediaSessionCompatToken, playerImpl, sharedPreferences); } @@ -105,7 +109,9 @@ public final class NotificationUtil { 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)"); + if (DEBUG) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification(true)"); + } notificationBuilder = createBackgroundPlayerNotification(context, mediaSessionCompatToken, basePlayerImpl, sharedPreferences); } @@ -119,7 +125,9 @@ public final class NotificationUtil { final boolean areOldNotificationsEnabled = sharedPreferences .getBoolean(context.getString(R.string.enable_old_notifications_key), false); if (notificationBuilder == null || areOldNotificationsEnabled) { - Log.d(TAG, "N_ recreateBackgroundPlayerNotification()"); + if (DEBUG) { + Log.d(TAG, "N_ recreateBackgroundPlayerNotification()"); + } notificationBuilder = createBackgroundPlayerNotification(context, mediaSessionCompatToken, basePlayerImpl, sharedPreferences); } 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 d0939a914..3bb2965a2 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 @@ -16,12 +16,14 @@ import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; +import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.player.mediasession.MediaSessionCallback; import org.schabi.newpipe.player.mediasession.PlayQueueNavigator; import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController; public class MediaSessionManager { private static final String TAG = "MediaSessionManager"; + public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); @NonNull private final MediaSessionCompat mediaSession; @@ -72,33 +74,35 @@ public class MediaSessionManager { return; } - 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()); + if (DEBUG) { + 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()); + } } 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() - ); + if (DEBUG) { + 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(); } } From 6de03f2bf0bf328fccf29bd7dd0d2407b0e1ed9b Mon Sep 17 00:00:00 2001 From: wb9688 Date: Fri, 31 Jul 2020 09:25:32 +0200 Subject: [PATCH 04/30] Fix crash when playing stream in background with shuffle in notification --- .../java/org/schabi/newpipe/player/NotificationUtil.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index d88a26b2f..f1b8356a8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -351,7 +351,7 @@ public final class NotificationUtil { } break; case "shuffle": - if (basePlayerImpl.playQueue.isShuffled()) { + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.isShuffled()) { builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", PendingIntent.getBroadcast(context, NOTIFICATION_ID_BACKGROUND, new Intent(BackgroundPlayer.ACTION_SHUFFLE), @@ -601,7 +601,7 @@ public final class NotificationUtil { } break; case "shuffle": - if (basePlayerImpl.playQueue.isShuffled()) { + if (basePlayerImpl.playQueue != null && basePlayerImpl.playQueue.isShuffled()) { notificationBuilder.mActions.set(slotNumber, new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, "ShuffleOn", PendingIntent.getBroadcast(context, @@ -835,7 +835,7 @@ public final class NotificationUtil { } break; case "shuffle": - if (playerImpl.playQueue.isShuffled()) { + if (playerImpl.playQueue != null && playerImpl.playQueue.isShuffled()) { builder.addAction(R.drawable.exo_controls_shuffle_on, "ShuffleOn", PendingIntent.getBroadcast(context, NOTIFICATION_ID_POPUP, new Intent(PopupVideoPlayer.ACTION_SHUFFLE), @@ -1080,7 +1080,7 @@ public final class NotificationUtil { } break; case "shuffle": - if (playerImpl.playQueue.isShuffled()) { + if (playerImpl.playQueue != null && playerImpl.playQueue.isShuffled()) { notificationBuilder.mActions.set(slotNumber, new NotificationCompat.Action(R.drawable.exo_controls_shuffle_on, "ShuffleOn", PendingIntent.getBroadcast(context, From 1a8ff8108709f15228712ad2942919d5c38662fa Mon Sep 17 00:00:00 2001 From: wb9688 Date: Mon, 3 Aug 2020 14:17:12 +0200 Subject: [PATCH 05/30] Use AppCompatImageButton to not crash on 4.4 --- app/src/main/res/layout-large-land/player.xml | 2 +- app/src/main/res/layout/player.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout-large-land/player.xml b/app/src/main/res/layout-large-land/player.xml index 9db51df26..3fa187906 100644 --- a/app/src/main/res/layout-large-land/player.xml +++ b/app/src/main/res/layout-large-land/player.xml @@ -465,7 +465,7 @@ android:layout_height="60dp" android:id="@+id/playQueueControl"> - - From adef9a8acfd8a141ac0393be16ba3d7ac619a3f4 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 15 Aug 2020 15:16:17 +0200 Subject: [PATCH 06/30] Rename notification functions: they are not background player only --- .../org/schabi/newpipe/player/MainPlayer.java | 2 +- .../newpipe/player/NotificationUtil.java | 15 ++++++------- .../newpipe/player/VideoPlayerImpl.java | 22 +++++++++---------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index 5569b8d06..92c6a3c86 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -244,7 +244,7 @@ public final class MainPlayer extends Service { } private void showNotificationAndStartForeground() { - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(playerImpl, true); + NotificationUtil.getInstance().recreateNotification(playerImpl, true); NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 0, false); NotificationUtil.getInstance().startForegroundServiceWithNotification(this); } diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index bfa698cd6..5f7398089 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -81,7 +81,7 @@ public final class NotificationUtil { // NOTIFICATION ///////////////////////////////////////////////////// - NotificationCompat.Builder createBackgroundPlayerNotification(final VideoPlayerImpl player) { + NotificationCompat.Builder createNotification(final VideoPlayerImpl player) { notificationManager = (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, @@ -183,10 +183,10 @@ public final class NotificationUtil { * @param player the player currently open, to take data from * @param playPauseDrawable if != -1, sets the drawable with that id on the play/pause button */ - synchronized void updateBackgroundPlayerNotification(final VideoPlayerImpl player, - @DrawableRes final int playPauseDrawable) { + synchronized void updateNotification(final VideoPlayerImpl player, + @DrawableRes final int playPauseDrawable) { if (DEBUG) { - Log.d(TAG, "N_ updateBackgroundPlayerNotification()"); + Log.d(TAG, "N_ updateNotification()"); } if (notificationBuilder == null) { @@ -232,15 +232,14 @@ public final class NotificationUtil { } } - void recreateBackgroundPlayerNotification(final VideoPlayerImpl player, - final boolean recreate) { + void recreateNotification(final VideoPlayerImpl player, final boolean recreate) { final boolean areOldNotificationsEnabled = player.sharedPreferences.getBoolean( player.context.getString(R.string.enable_old_notifications_key), false); if (notificationBuilder == null || recreate || areOldNotificationsEnabled) { if (DEBUG) { - Log.d(TAG, "N_ recreateBackgroundPlayerNotification(true)"); + Log.d(TAG, "N_ recreateNotification(true)"); } - notificationBuilder = createBackgroundPlayerNotification(player); + notificationBuilder = createNotification(player); } timesNotificationUpdated = 0; } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 1c89cc4b1..e9430be0b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -652,7 +652,7 @@ public class VideoPlayerImpl extends VideoPlayer } if (NotificationUtil.getInstance().shouldRecreateOldNotification()) { - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(this, false); + NotificationUtil.getInstance().recreateNotification(this, false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); @@ -664,7 +664,7 @@ public class VideoPlayerImpl extends VideoPlayer NotificationUtil.getInstance().setProgressbarOnOldNotifications(duration, currentProgress, false); - NotificationUtil.getInstance().updateBackgroundPlayerNotification(this, -1); + NotificationUtil.getInstance().updateNotification(this, -1); } } @@ -1083,8 +1083,8 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(false); - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(this, false); - NotificationUtil.getInstance().updateBackgroundPlayerNotification( + NotificationUtil.getInstance().recreateNotification(this, false); + NotificationUtil.getInstance().updateNotification( this, R.drawable.ic_play_arrow_white_24dp); } @@ -1101,7 +1101,7 @@ public class VideoPlayerImpl extends VideoPlayer isForwardPressed = false; isRewindPressed = false; } else { - NotificationUtil.getInstance().updateBackgroundPlayerNotification(this, -1); + NotificationUtil.getInstance().updateNotification(this, -1); } } } @@ -1154,8 +1154,8 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(this, false); - NotificationUtil.getInstance().updateBackgroundPlayerNotification( + NotificationUtil.getInstance().recreateNotification(this, false); + NotificationUtil.getInstance().updateNotification( this, R.drawable.ic_play_arrow_white_24dp); } @@ -1170,10 +1170,10 @@ public class VideoPlayerImpl extends VideoPlayer getRootView().setKeepScreenOn(false); updateWindowFlags(IDLE_WINDOW_FLAGS); - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(this, false); + NotificationUtil.getInstance().recreateNotification(this, false); NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 100, false); NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); - NotificationUtil.getInstance().updateBackgroundPlayerNotification( + NotificationUtil.getInstance().updateNotification( this, R.drawable.ic_replay_white_24dp); super.onCompleted(); @@ -1335,9 +1335,9 @@ public class VideoPlayerImpl extends VideoPlayer //////////////////////////////////////////////////////////////////////////*/ void resetNotification(final boolean recreate, @DrawableRes final int drawableId) { - NotificationUtil.getInstance().recreateBackgroundPlayerNotification(this, recreate); + NotificationUtil.getInstance().recreateNotification(this, recreate); NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); - NotificationUtil.getInstance().updateBackgroundPlayerNotification(this, drawableId); + NotificationUtil.getInstance().updateNotification(this, drawableId); } @Override From e08480f34532daca4c8f432ef902454869b1a719 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 15 Aug 2020 19:15:08 +0200 Subject: [PATCH 07/30] Completely remove old player notification --- .../org/schabi/newpipe/player/MainPlayer.java | 3 +- .../newpipe/player/NotificationUtil.java | 400 ++++++------------ .../newpipe/player/VideoPlayerImpl.java | 65 +-- .../settings/AppearanceSettingsFragment.java | 11 - app/src/main/res/values/settings_keys.xml | 1 - app/src/main/res/values/strings.xml | 2 - app/src/main/res/xml/appearance_settings.xml | 7 - 7 files changed, 140 insertions(+), 349 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index 92c6a3c86..065efca02 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -244,8 +244,7 @@ public final class MainPlayer extends Service { } private void showNotificationAndStartForeground() { - NotificationUtil.getInstance().recreateNotification(playerImpl, true); - NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 0, false); + NotificationUtil.getInstance().createNotificationIfNeeded(playerImpl, true); NotificationUtil.getInstance().startForegroundServiceWithNotification(this); } diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 5f7398089..32dd93bd4 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -7,16 +7,13 @@ import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Matrix; -import android.os.Build; import android.util.Log; -import android.widget.RemoteViews; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; -import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.util.NavigationHelper; @@ -34,8 +31,6 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS; import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT; import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE; -import static org.schabi.newpipe.player.MainPlayer.SET_IMAGE_RESOURCE_METHOD; -import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; /** * This is a utility class for player notifications. @@ -46,8 +41,6 @@ public final class NotificationUtil { private static final String TAG = "NotificationUtil"; private static final boolean DEBUG = BasePlayer.DEBUG; private static final int NOTIFICATION_ID = 123789; - // only used for old notifications - private static final int NOTIFICATION_UPDATES_BEFORE_RESET = 60; @Nullable private static NotificationUtil instance = null; @@ -58,14 +51,8 @@ public final class NotificationUtil { private String notificationSlot4 = "close"; private NotificationManager notificationManager; - private RemoteViews notificationRemoteView; // always null when new notifications are used - private RemoteViews bigNotificationRemoteView; // always null when new notifications are used private NotificationCompat.Builder notificationBuilder; - private int cachedDuration; // only used for old notifications - private String cachedDurationString; // only used for old notifications - private int timesNotificationUpdated; // only used for old notifications - private NotificationUtil() { } @@ -81,110 +68,13 @@ public final class NotificationUtil { // NOTIFICATION ///////////////////////////////////////////////////// - NotificationCompat.Builder createNotification(final VideoPlayerImpl player) { - notificationManager = - (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); - NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, - player.context.getString(R.string.notification_channel_id)); - - final boolean areOldNotificationsEnabled = player.sharedPreferences.getBoolean( - player.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_notification); - bigNotificationRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, - R.layout.player_notification_expanded); - - setupOldNotification(notificationRemoteView, player); - setupOldNotification(bigNotificationRemoteView, player); - - builder - .setOngoing(true) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setCustomContentView(notificationRemoteView) - .setCustomBigContentView(bigNotificationRemoteView) - .setPriority(NotificationCompat.PRIORITY_MAX); - } else { - final String compactView = player.sharedPreferences.getString(player.context.getString( - R.string.settings_notifications_compact_view_key), "0,1,2"); - int compactSlot0 = 0; - int compactSlot1 = 1; - int compactSlot2 = 2; - try { - if (compactView != null) { - final 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(); - } - - builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle() - .setMediaSession(player.mediaSessionManager.getSessionToken()) - .setShowCancelButton(false) - .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) - .setOngoing(false) - .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, - getIntentForNotification(player), FLAG_UPDATE_CURRENT)) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentTitle(player.getVideoTitle()) - .setContentText(player.getUploaderName()) - .setDeleteIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, - new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)) - .setColor(ContextCompat.getColor(player.context, R.color.gray)) - .setPriority(NotificationCompat.PRIORITY_HIGH); - final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( - player.context.getString(R.string.scale_to_square_image_in_notifications_key), - false); - if (scaleImageToSquareAspectRatio) { - builder.setLargeIcon(getBitmapWithSquareAspectRatio(player.getThumbnail())); - } else { - builder.setLargeIcon(player.getThumbnail()); - } - - notificationSlot0 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_0_key), notificationSlot0); - notificationSlot1 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_1_key), notificationSlot1); - notificationSlot2 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_2_key), notificationSlot2); - notificationSlot3 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_3_key), notificationSlot3); - notificationSlot4 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_4_key), notificationSlot4); - - addAction(builder, player, notificationSlot0); - addAction(builder, player, notificationSlot1); - addAction(builder, player, notificationSlot2); - addAction(builder, player, notificationSlot3); - addAction(builder, player, notificationSlot4); - } - - return builder; - } - /** * Updates the notification, and the button icons depending on the playback state. * On old notifications used for changes on the remoteView * * @param player the player currently open, to take data from - * @param playPauseDrawable if != -1, sets the drawable with that id on the play/pause button */ - synchronized void updateNotification(final VideoPlayerImpl player, - @DrawableRes final int playPauseDrawable) { + synchronized void updateNotification(final VideoPlayerImpl player) { if (DEBUG) { Log.d(TAG, "N_ updateNotification()"); } @@ -192,56 +82,116 @@ public final class NotificationUtil { if (notificationBuilder == null) { return; } - if (playPauseDrawable != -1) { - if (notificationRemoteView != null) { - notificationRemoteView - .setImageViewResource(R.id.notificationPlayPause, playPauseDrawable); - } - if (bigNotificationRemoteView != null) { - bigNotificationRemoteView - .setImageViewResource(R.id.notificationPlayPause, playPauseDrawable); - } + + notificationBuilder.setContentTitle(player.getVideoTitle()); + notificationBuilder.setContentText(player.getUploaderName()); + final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( + player.context.getString(R.string.scale_to_square_image_in_notifications_key), + false); + if (scaleImageToSquareAspectRatio) { + notificationBuilder.setLargeIcon( + getBitmapWithSquareAspectRatio(player.getThumbnail())); + } else { + notificationBuilder.setLargeIcon(player.getThumbnail()); } - final boolean areOldNotificationsEnabled = player.sharedPreferences.getBoolean( - player.context.getString(R.string.enable_old_notifications_key), false); - if (!areOldNotificationsEnabled) { - notificationBuilder.setContentTitle(player.getVideoTitle()); - notificationBuilder.setContentText(player.getUploaderName()); - final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( - player.context.getString(R.string.scale_to_square_image_in_notifications_key), - false); - if (scaleImageToSquareAspectRatio) { - notificationBuilder.setLargeIcon( - getBitmapWithSquareAspectRatio(player.getThumbnail())); - } else { - notificationBuilder.setLargeIcon(player.getThumbnail()); - } - - setAction(player, notificationSlot0, 0); - setAction(player, notificationSlot1, 1); - setAction(player, notificationSlot2, 2); - setAction(player, notificationSlot3, 3); - setAction(player, notificationSlot4, 4); - } + setAction(player, notificationSlot0, 0); + setAction(player, notificationSlot1, 1); + setAction(player, notificationSlot2, 2); + setAction(player, notificationSlot3, 3); + setAction(player, notificationSlot4, 4); notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); - - if (areOldNotificationsEnabled) { - timesNotificationUpdated++; - } } - void recreateNotification(final VideoPlayerImpl player, final boolean recreate) { - final boolean areOldNotificationsEnabled = player.sharedPreferences.getBoolean( - player.context.getString(R.string.enable_old_notifications_key), false); - if (notificationBuilder == null || recreate || areOldNotificationsEnabled) { + /** + * Creates the notification, if it does not exist already or unless forceRecreate is true. + * @param player the player currently open, to take data from + * @param forceRecreate whether to force the recreation of the notification even if it already + * exists + */ + void createNotificationIfNeeded(final VideoPlayerImpl player, final boolean forceRecreate) { + if (notificationBuilder == null || forceRecreate) { if (DEBUG) { - Log.d(TAG, "N_ recreateNotification(true)"); + Log.d(TAG, "N_ createNotificationIfNeeded(true)"); } notificationBuilder = createNotification(player); } - timesNotificationUpdated = 0; + } + + private NotificationCompat.Builder createNotification(final VideoPlayerImpl player) { + notificationManager = + (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); + NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, + player.context.getString(R.string.notification_channel_id)); + + final String compactView = player.sharedPreferences.getString(player.context.getString( + R.string.settings_notifications_compact_view_key), "0,1,2"); + int compactSlot0 = 0; + int compactSlot1 = 1; + int compactSlot2 = 2; + try { + if (compactView != null) { + final 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(); + } + + builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(player.mediaSessionManager.getSessionToken()) + .setShowCancelButton(false) + .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) + .setOngoing(false) + .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, + getIntentForNotification(player), FLAG_UPDATE_CURRENT)) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(player.getVideoTitle()) + .setContentText(player.getUploaderName()) + .setDeleteIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, + new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)) + .setColor(ContextCompat.getColor(player.context, R.color.gray)) + .setPriority(NotificationCompat.PRIORITY_HIGH); + final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( + player.context.getString(R.string.scale_to_square_image_in_notifications_key), + false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(player.getThumbnail())); + } else { + builder.setLargeIcon(player.getThumbnail()); + } + + notificationSlot0 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_4_key), notificationSlot4); + + addAction(builder, player, notificationSlot0); + addAction(builder, player, notificationSlot1); + addAction(builder, player, notificationSlot2); + addAction(builder, player, notificationSlot3); + addAction(builder, player, notificationSlot4); + + return builder; } @@ -251,65 +201,18 @@ public final class NotificationUtil { boolean hasSlotWithBuffering() { - return notificationSlot0.contains("buffering") - || notificationSlot1.contains("buffering") - || notificationSlot2.contains("buffering") - || notificationSlot3.contains("buffering") - || notificationSlot4.contains("buffering"); - } - - - ///////////////////////////////////////////////////// - // OLD NOTIFICATION - ///////////////////////////////////////////////////// - - @Deprecated - boolean shouldRecreateOldNotification() { - return timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET; - } - - /** - * @param bitmap if null, the thumbnail will be removed - */ - @Deprecated // only used for old notifications - void updateOldNotificationsThumbnail(@Nullable final Bitmap bitmap) { - if (notificationRemoteView != null) { - notificationRemoteView.setImageViewBitmap(R.id.notificationCover, bitmap); - } - if (bigNotificationRemoteView != null) { - bigNotificationRemoteView.setImageViewBitmap(R.id.notificationCover, bitmap); - } - } - - @Deprecated // only used for old notifications - 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 - 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); - } + return notificationSlot0.equals("play_pause_buffering") + || notificationSlot1.equals("play_pause_buffering") + || notificationSlot2.equals("play_pause_buffering") + || notificationSlot3.equals("play_pause_buffering") + || notificationSlot4.equals("play_pause_buffering"); } public void cancelNotification() { try { if (notificationManager != null) { notificationManager.cancel(NOTIFICATION_ID); + notificationManager = null; } } catch (Exception e) { Log.e("NotificationUtil", "Exception", e); @@ -317,64 +220,6 @@ public final class NotificationUtil { } - ///////////////////////////////////////////////////// - // OLD NOTIFICATION UTILS - ///////////////////////////////////////////////////// - - @Deprecated // only used for old notifications - private void setupOldNotification(final RemoteViews remoteViews, - final VideoPlayerImpl player) { - remoteViews.setTextViewText(R.id.notificationSongName, player.getVideoTitle()); - remoteViews.setTextViewText(R.id.notificationArtist, player.getUploaderName()); - remoteViews.setImageViewBitmap(R.id.notificationCover, player.getThumbnail()); - - remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PAUSE), FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationStop, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - new Intent(ACTION_REPEAT), FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationContent, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - getIntentForNotification(player), FLAG_UPDATE_CURRENT)); - - if (player.playQueue != null && player.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(player.context, NOTIFICATION_ID, - new Intent(ACTION_PLAY_PREVIOUS), FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - new Intent(ACTION_PLAY_NEXT), 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(player.context, NOTIFICATION_ID, - new Intent(ACTION_FAST_REWIND), FLAG_UPDATE_CURRENT)); - remoteViews.setOnClickPendingIntent(R.id.notificationFForward, - PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, - new Intent(ACTION_FAST_FORWARD), FLAG_UPDATE_CURRENT)); - } - - setRepeatModeIcon(remoteViews, player.getRepeatMode()); - } - - @Deprecated // only used for old notifications - private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { - remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, - getRepeatModeDrawable(repeatMode)); - } - - ///////////////////////////////////////////////////// // ACTIONS ///////////////////////////////////////////////////// @@ -401,11 +246,11 @@ public final class NotificationUtil { || player.getCurrentState() == BasePlayer.STATE_BLOCKED || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { builder.setSmallIcon(android.R.drawable.stat_sys_download); - return getAction(builder, player, R.drawable.ic_file_download_white_24dp, + return getAction(player, R.drawable.ic_file_download_white_24dp, "Buffering", ACTION_BUFFERING); } else { builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); - return getAction(builder, player, + return getAction(player, player.isPlaying() ? R.drawable.exo_notification_pause : R.drawable.exo_notification_play, player.isPlaying() ? "Pause" : "Play", @@ -416,51 +261,51 @@ public final class NotificationUtil { || player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED || player.getCurrentState() == BasePlayer.STATE_BUFFERING; - return getAction(builder, player, + return getAction(player, pauseOrPlay ? R.drawable.exo_notification_pause : R.drawable.exo_notification_play, pauseOrPlay ? "Pause" : "Play", ACTION_PLAY_PAUSE); case "rewind": - return getAction(builder, player, R.drawable.exo_controls_rewind, + return getAction(player, R.drawable.exo_controls_rewind, "Rewind", ACTION_FAST_REWIND); case "smart_rewind_prev": if (player.playQueue != null && player.playQueue.size() > 1) { - return getAction(builder, player, R.drawable.exo_notification_previous, + return getAction(player, R.drawable.exo_notification_previous, "Prev", ACTION_PLAY_PREVIOUS); } else { - return getAction(builder, player, R.drawable.exo_controls_rewind, + return getAction(player, R.drawable.exo_controls_rewind, "Rewind", ACTION_FAST_REWIND); } case "forward": - return getAction(builder, player, R.drawable.exo_controls_fastforward, + return getAction(player, R.drawable.exo_controls_fastforward, "Forward", ACTION_FAST_FORWARD); case "smart_forward_next": if (player.playQueue != null && player.playQueue.size() > 1) { - return getAction(builder, player, R.drawable.exo_notification_next, + return getAction(player, R.drawable.exo_notification_next, "Next", ACTION_PLAY_NEXT); } else { - return getAction(builder, player, R.drawable.exo_controls_fastforward, + return getAction(player, R.drawable.exo_controls_fastforward, "Forward", ACTION_FAST_FORWARD); } case "next": - return getAction(builder, player, R.drawable.exo_notification_next, + return getAction(player, R.drawable.exo_notification_next, "Next", ACTION_PLAY_NEXT); case "prev": - return getAction(builder, player, R.drawable.exo_notification_previous, + return getAction(player, R.drawable.exo_notification_previous, "Prev", ACTION_PLAY_PREVIOUS); case "repeat": - return getAction(builder, player, getRepeatModeDrawable(player.getRepeatMode()), + return getAction(player, getRepeatModeDrawable(player.getRepeatMode()), getRepeatModeTitle(player.getRepeatMode()), ACTION_REPEAT); case "shuffle": final boolean shuffled = player.playQueue != null && player.playQueue.isShuffled(); - return getAction(builder, player, + return getAction(player, shuffled ? R.drawable.exo_controls_shuffle_on : R.drawable.exo_controls_shuffle_off, shuffled ? "ShuffleOn" : "ShuffleOff", ACTION_SHUFFLE); case "close": - return getAction(builder, player, R.drawable.ic_close_white_24dp, + return getAction(player, R.drawable.ic_close_white_24dp, "Close", ACTION_CLOSE); case "n/a": default: @@ -469,8 +314,7 @@ public final class NotificationUtil { } } - private NotificationCompat.Action getAction(final NotificationCompat.Builder builder, - final VideoPlayerImpl player, + private NotificationCompat.Action getAction(final VideoPlayerImpl player, @DrawableRes final int drawable, final String title, final String intentAction) { diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index e9430be0b..e9c8fd89b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -57,7 +57,6 @@ import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; -import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -577,7 +576,7 @@ public class VideoPlayerImpl extends VideoPlayer void onShuffleOrRepeatModeChanged() { updatePlaybackButtons(); updatePlayback(); - resetNotification(false, -1); + resetNotification(false); } @Override @@ -614,7 +613,7 @@ public class VideoPlayerImpl extends VideoPlayer titleTextView.setText(tag.getMetadata().getName()); channelTextView.setText(tag.getMetadata().getUploaderName()); - resetNotification(false, -1); + resetNotification(false); updateMetadata(); } @@ -642,30 +641,6 @@ public class VideoPlayerImpl extends VideoPlayer // setMetadata only updates the metadata when any of the metadata keys are null mediaSessionManager.setMetadata(getVideoTitle(), getUploaderName(), getThumbnail(), duration); - - final boolean areOldNotificationsEnabled = sharedPreferences.getBoolean( - context.getString(R.string.enable_old_notifications_key), false); - if (areOldNotificationsEnabled) { - if (!shouldUpdateOnProgress || getCurrentState() == BasePlayer.STATE_COMPLETED - || getCurrentState() == BasePlayer.STATE_PAUSED || getPlayQueue() == null) { - return; - } - - if (NotificationUtil.getInstance().shouldRecreateOldNotification()) { - NotificationUtil.getInstance().recreateNotification(this, false); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); - } - } - - - NotificationUtil.getInstance().setCachedDuration(currentProgress, duration); - NotificationUtil.getInstance().setProgressbarOnOldNotifications(duration, - currentProgress, false); - - NotificationUtil.getInstance().updateNotification(this, -1); - } } @Override @@ -1083,9 +1058,9 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(false); - NotificationUtil.getInstance().recreateNotification(this, false); + NotificationUtil.getInstance().createNotificationIfNeeded(this, false); NotificationUtil.getInstance().updateNotification( - this, R.drawable.ic_play_arrow_white_24dp); + this); } @Override @@ -1101,7 +1076,7 @@ public class VideoPlayerImpl extends VideoPlayer isForwardPressed = false; isRewindPressed = false; } else { - NotificationUtil.getInstance().updateNotification(this, -1); + NotificationUtil.getInstance().updateNotification(this); } } } @@ -1121,7 +1096,7 @@ public class VideoPlayerImpl extends VideoPlayer checkLandscape(); getRootView().setKeepScreenOn(true); - resetNotification(false, R.drawable.ic_pause_white_24dp); + resetNotification(false); } @Override @@ -1137,7 +1112,7 @@ public class VideoPlayerImpl extends VideoPlayer updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(false, R.drawable.ic_play_arrow_white_24dp); + resetNotification(false); // Remove running notification when user don't want music (or video in popup) // to be played in background @@ -1154,9 +1129,9 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); - NotificationUtil.getInstance().recreateNotification(this, false); + NotificationUtil.getInstance().createNotificationIfNeeded(this, false); NotificationUtil.getInstance().updateNotification( - this, R.drawable.ic_play_arrow_white_24dp); + this); } @@ -1170,11 +1145,8 @@ public class VideoPlayerImpl extends VideoPlayer getRootView().setKeepScreenOn(false); updateWindowFlags(IDLE_WINDOW_FLAGS); - NotificationUtil.getInstance().recreateNotification(this, false); - NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 100, false); - NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); - NotificationUtil.getInstance().updateNotification( - this, R.drawable.ic_replay_white_24dp); + NotificationUtil.getInstance().createNotificationIfNeeded(this, false); + NotificationUtil.getInstance().updateNotification(this); super.onCompleted(); } @@ -1182,8 +1154,6 @@ public class VideoPlayerImpl extends VideoPlayer @Override public void destroy() { super.destroy(); - - NotificationUtil.getInstance().updateOldNotificationsThumbnail(null); service.getContentResolver().unregisterContentObserver(settingsContentObserver); } @@ -1334,10 +1304,9 @@ public class VideoPlayerImpl extends VideoPlayer // Thumbnail Loading //////////////////////////////////////////////////////////////////////////*/ - void resetNotification(final boolean recreate, @DrawableRes final int drawableId) { - NotificationUtil.getInstance().recreateNotification(this, recreate); - NotificationUtil.getInstance().updateOldNotificationsThumbnail(getThumbnail()); - NotificationUtil.getInstance().updateNotification(this, drawableId); + void resetNotification(final boolean recreate) { + NotificationUtil.getInstance().createNotificationIfNeeded(this, recreate); + NotificationUtil.getInstance().updateNotification(this); } @Override @@ -1347,7 +1316,7 @@ public class VideoPlayerImpl extends VideoPlayer // rebuild OLD notification here since remote view does not release bitmaps, // causing memory leaks super.onLoadingComplete(imageUri, view, loadedImage); - resetNotification(true, -1); + resetNotification(true); } @Override @@ -1355,13 +1324,13 @@ public class VideoPlayerImpl extends VideoPlayer final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(true, -1); + resetNotification(true); } @Override public void onLoadingCancelled(final String imageUri, final View view) { super.onLoadingCancelled(imageUri, view); - resetNotification(true, -1); + resetNotification(true); } /*////////////////////////////////////////////////////////////////////////// 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 564e3c5ce..a9531693c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -11,7 +11,6 @@ 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 { @@ -53,18 +52,8 @@ 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) -> { - // kill player notification - NotificationUtil.getInstance().cancelNotification(); - return true; - }; - @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResource(R.xml.appearance_settings); diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 98b62238a..36dde583c 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -114,7 +114,6 @@ 144p - enable_old_notifications notifications_compact_view 0,1,2 scale_to_square_image_in_notifications diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce0ac40ec..20b6b81f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,8 +58,6 @@ org.xbmc.kore Show \"Play with Kodi\" option Display an option to play a video via Kodi media center - 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 diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml index a77663843..3cdaf8e9e 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -39,13 +39,6 @@ android:title="@string/settings_category_notifications_title" app:iconSpaceReserved="false"> - - Date: Sat, 15 Aug 2020 23:45:23 +0200 Subject: [PATCH 08/30] Show hourglass icon when buffering --- .../newpipe/player/NotificationUtil.java | 72 +++++++++--------- .../ic_hourglass_top_white_24dp.png | Bin 0 -> 302 bytes .../ic_hourglass_top_white_24dp.png | Bin 0 -> 246 bytes .../ic_hourglass_top_white_24dp.png | Bin 0 -> 413 bytes .../ic_hourglass_top_white_24dp.png | Bin 0 -> 614 bytes .../ic_hourglass_top_white_24dp.png | Bin 0 -> 777 bytes 6 files changed, 36 insertions(+), 36 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp.png diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 32dd93bd4..3b7e2ac3d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -94,12 +94,7 @@ public final class NotificationUtil { } else { notificationBuilder.setLargeIcon(player.getThumbnail()); } - - setAction(player, notificationSlot0, 0); - setAction(player, notificationSlot1, 1); - setAction(player, notificationSlot2, 2); - setAction(player, notificationSlot3, 3); - setAction(player, notificationSlot4, 4); + updateActions(notificationBuilder, player); notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); } @@ -122,7 +117,7 @@ public final class NotificationUtil { private NotificationCompat.Builder createNotification(final VideoPlayerImpl player) { notificationManager = (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); - NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, + final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, player.context.getString(R.string.notification_channel_id)); final String compactView = player.sharedPreferences.getString(player.context.getString( @@ -165,6 +160,7 @@ public final class NotificationUtil { new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)) .setColor(ContextCompat.getColor(player.context, R.color.gray)) .setPriority(NotificationCompat.PRIORITY_HIGH); + final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( player.context.getString(R.string.scale_to_square_image_in_notifications_key), false); @@ -174,22 +170,8 @@ public final class NotificationUtil { builder.setLargeIcon(player.getThumbnail()); } - notificationSlot0 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_0_key), notificationSlot0); - notificationSlot1 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_1_key), notificationSlot1); - notificationSlot2 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_2_key), notificationSlot2); - notificationSlot3 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_3_key), notificationSlot3); - notificationSlot4 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_4_key), notificationSlot4); - - addAction(builder, player, notificationSlot0); - addAction(builder, player, notificationSlot1); - addAction(builder, player, notificationSlot2); - addAction(builder, player, notificationSlot3); - addAction(builder, player, notificationSlot4); + initializeNotificationSlots(player); + updateActions(builder, player); return builder; } @@ -224,32 +206,50 @@ public final class NotificationUtil { // ACTIONS ///////////////////////////////////////////////////// - private void addAction(final NotificationCompat.Builder builder, - final VideoPlayerImpl player, - final String slot) { - builder.addAction(getAction(builder, player, slot)); + private void initializeNotificationSlots(final VideoPlayerImpl player) { + notificationSlot0 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_0_key), notificationSlot0); + notificationSlot1 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_1_key), notificationSlot1); + notificationSlot2 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_2_key), notificationSlot2); + notificationSlot3 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_3_key), notificationSlot3); + notificationSlot4 = player.sharedPreferences.getString( + player.context.getString(R.string.notification_slot_4_key), notificationSlot4); } @SuppressLint("RestrictedApi") - private void setAction(final VideoPlayerImpl player, - final String slot, - final int slotNumber) { - notificationBuilder.mActions.set(slotNumber, getAction(notificationBuilder, player, slot)); + private void updateActions(final NotificationCompat.Builder builder, + final VideoPlayerImpl player) { + builder.mActions.clear(); + addAction(builder, player, notificationSlot0); + addAction(builder, player, notificationSlot1); + addAction(builder, player, notificationSlot2); + addAction(builder, player, notificationSlot3); + addAction(builder, player, notificationSlot4); } - private NotificationCompat.Action getAction(final NotificationCompat.Builder builder, - final VideoPlayerImpl player, + private void addAction(final NotificationCompat.Builder builder, + final VideoPlayerImpl player, + final String slot) { + final NotificationCompat.Action action = getAction(player, slot); + if (action != null) { + builder.addAction(action); + } + } + + @Nullable + private NotificationCompat.Action getAction(final VideoPlayerImpl player, final String slot) { switch (slot) { case "play_pause_buffering": if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { - builder.setSmallIcon(android.R.drawable.stat_sys_download); - return getAction(player, R.drawable.ic_file_download_white_24dp, + return getAction(player, R.drawable.ic_hourglass_top_white_24dp, "Buffering", ACTION_BUFFERING); } else { - builder.setSmallIcon(R.drawable.ic_newpipe_triangle_white); return getAction(player, player.isPlaying() ? R.drawable.exo_notification_pause : R.drawable.exo_notification_play, diff --git a/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..13050da089b68bb561b1b88c72a2f23b7a018abf GIT binary patch literal 302 zcmV+}0nz@6P)Nkl%kOb+SFg8yf4J1JA4k(RzB1HQTxKi^J_m z=)PuW{QLo9{cWs9n}O7q&!^F!#XbS_4xm0ZND8`wMg+KnZum6#-uly6KMrp1a^B9PMKK<0)2JsR0Z$Rg|+q0d;Uf;&U3R@|(^8f$<07*qoM6N<$f{e0+ Ak^lez literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2343e8cb9e297af98a685569a634ee2fe2a422d7 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjdp%toLn>}1CrE62@c;k+MGk9x zRm4<}H?{pg(%crv-L{PNpTnQ29ETmV{+!&{AlPxa;Rm% zW05+kz--6#(N*D^)1=EyU9~N%5><{f@hkk}G4+t{-eVTRx8zv!q#m(ucV8XTq9P4B u(}m85nvxy%aIxE&t>Cu46*kiu=!^C3ukHU_nkEbM2!p4qpUXO@geCyZ!C@@` literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..bdf88fb3b6228c30f5d7dec80785280edab4f951 GIT binary patch literal 413 zcmV;O0b>4%P)O7Aq9@TonZ1cVp_@8>K<>;J^DzwVmC38< z1LtD=zN+(ktciUv|18=ma~ILOXrb~JICfEaJFJaew~aP`7QKnmj{Fqo&IW}2cEZ4# zZF7RbWzV`La!wuX6xsyHjDc;h{A$43RmYuZYGpP73Zv(HuYAv)&@)lE+R%J;_56Su zDb_mQk!?oQ*${vL1aJ?q9r00P%lASAcu;`H{>aB*F#L%hbZ+LFA=;ok00000NkvXX Hu0mjf8wtON literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a097f4de3976b2b71e70580fdd791fd281ca8d GIT binary patch literal 614 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@Zgyv2VAAz;aSW-r^>(&l-XRAOmz5he zHZbu%=U~#364rK-|KAp?`=@>T(7mG}{@|2&8;jy! zt+OoKd@en>*HJFnbaUY|h3_+qRabOY=-Q<+Ja35-zCCXy?@>p#hAqb~9Bc0?ed1d1 z*vBey&9xQNb3_z)KMLMldS;I4v0I$SCwR^lX)wE%>c_NTV#QRZ3Gb|b@h7Z$B$xMY zn<0n&4Y@~Omu|bUE_t=t;g=oHA2ll`PuubP+efQ;f$TkRBRDzJ>+8-gwSG9wA^S_t znK>5>iu-=-`k+^)_pN+GZe#5uW6do}lb<$aDNG*)EF|P6HvpDckwDj1G?-?tMo%@Rf87m*1*9rS46sEPcJ$%c|$BODohp%cm z?AxK4wM#-+VqKnKRprk4o+sv*oy$tuaX#`9*V8)zw-4S}`p{0(>3rEbW6p(^)lW{v zAM#rIE$^x1h4}tjyCY|Lewzedp7lJb${}C!TcxwFHG}O2d6WqHD4zC*Qw}V2VQ<5a i{D+`ebq$>MLi|jkkK9kjrLMsA!{F)a=d#Wzp$Pz@f(ADL literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7a099dabfe78a342fb0ab8821a1344cbdc805c18 GIT binary patch literal 777 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V7lPx;uunK>+RhASxk-s?e7_N zm@+RdHCocWv|r(6@F~$s21l0p3QN8Swk+`$R%+VntMfLOd843C^VFNF`zO?|+v$9M z_wB!R1`6}cH=EzT^Uji6%9TU0MZgJ6{L9=MXFug+4NHI)BhXHwK!6&9bMj-MC-?da zUnn=s+qheZVMFqppBxX4+-TJ+h`DfT|78h(C3#g2TVHqA%K^!sjeqWUxKgrlGLuT( zqK#J;1*GVGyAyH!#(^7$ZfLb1Suaw(VB;M3g(qW5oUfXnW<21tQ95wNr4SM6>L8~u z_EmpoCLMG1wT*T-vg+7TMg|=b?Y;~B)(aQ@teEt%$vQwstDd=I-6~`4$osKId;h*% zG0pLIlFt0C3iHdjE^Pa-d2c||w$}e|Tbs7)9?H~DVR&#O;&enkJ45YH3$?{BC)cOF zxprf*@=kBt%lcocCd)m2%){}LVat>q_2SoBxVL@1s_64X!O8i3mh~%MZR51N z_2|9F{LI&Le6tVDPP(gIW}3oK8v3|ozO*{0dDXJ6SpKh>%UAxac(>xu(!TO7C&Ge$ ztbZ}5uSQ3<{7}sC*^aidnXfW)BMZK7Dx6fXIDN^+5ND5?OWzK~{r<-JUGh&}^pYI? z8!fT#4Zm%zX}i76<8s`I0-lH4bT4fYHzBe6Gzc(!aL+@ex1X Date: Sun, 16 Aug 2020 00:01:43 +0200 Subject: [PATCH 09/30] Extract hardcoded strings into strings.xml and improve them --- .../newpipe/player/NotificationUtil.java | 10 +- app/src/main/res/values/settings_keys.xml | 172 +++++++++--------- app/src/main/res/values/strings.xml | 33 +++- app/src/main/res/xml/appearance_settings.xml | 58 +++--- 4 files changed, 144 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 3b7e2ac3d..96ade4d7e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -208,15 +208,15 @@ public final class NotificationUtil { private void initializeNotificationSlots(final VideoPlayerImpl player) { notificationSlot0 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_0_key), notificationSlot0); + player.context.getString(R.string.notification_action_0_key), notificationSlot0); notificationSlot1 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_1_key), notificationSlot1); + player.context.getString(R.string.notification_action_1_key), notificationSlot1); notificationSlot2 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_2_key), notificationSlot2); + player.context.getString(R.string.notification_action_2_key), notificationSlot2); notificationSlot3 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_3_key), notificationSlot3); + player.context.getString(R.string.notification_action_3_key), notificationSlot3); notificationSlot4 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_slot_4_key), notificationSlot4); + player.context.getString(R.string.notification_action_4_key), notificationSlot4); } @SuppressLint("RestrictedApi") diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 36dde583c..6099c6ecf 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -118,107 +118,107 @@ 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 + 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 + notification_action_0_key + @string/notification_action_smart_rewind_prev_key + + @string/notification_action_smart_rewind_prev_value + @string/notification_action_prev_value + @string/notification_action_rewind_value - - @string/notification_slot_smart_rewind_prev_key - @string/notification_slot_prev_key - @string/notification_slot_rewind_key + + @string/notification_action_smart_rewind_prev_key + @string/notification_action_prev_key + @string/notification_action_rewind_key - notification_slot_1_key - @string/notification_slot_play_pause_buffering_key - - Play / Pause / Buffering - Play / Pause - Rewind + notification_action_1_key + @string/notification_action_play_pause_buffering_key + + @string/notification_action_play_pause_buffering_value + @string/notification_action_play_pause_value + @string/notification_action_rewind_value - - @string/notification_slot_play_pause_buffering_key - @string/notification_slot_play_pause_key - @string/notification_slot_rewind_key + + @string/notification_action_play_pause_buffering_key + @string/notification_action_play_pause_key + @string/notification_action_rewind_key - notification_slot_2_key - @string/notification_slot_smart_forward_next_key - - Forward / Next - Forward - Next - Play / Pause / Buffering - Play / Pause + notification_action_2_key + @string/notification_action_smart_forward_next_key + + @string/notification_action_smart_forward_next_value + @string/notification_action_forward_value + @string/notification_action_next_value + @string/notification_action_play_pause_buffering_value + @string/notification_action_play_pause_value - - @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 + + @string/notification_action_smart_forward_next_key + @string/notification_action_forward_key + @string/notification_action_next_key + @string/notification_action_play_pause_buffering_key + @string/notification_action_play_pause_key - notification_slot_3_key - @string/notification_slot_repeat_key - - Repeat - Shuffle - Previous - Forward - Forward / Next - Rewind - Rewind / Previous - Close - N/A + notification_action_3_key + @string/notification_action_repeat_key + + @string/notification_action_repeat_value + @string/notification_action_shuffle_value + @string/notification_action_prev_value + @string/notification_action_forward_value + @string/notification_action_smart_forward_next_value + @string/notification_action_rewind_value + @string/notification_action_smart_rewind_prev_value + @string/notification_action_close_value + @string/notification_action_n_a_value - - @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 + + @string/notification_action_repeat_key + @string/notification_action_shuffle_key + @string/notification_action_prev_key + @string/notification_action_forward_key + @string/notification_action_smart_forward_next_key + @string/notification_action_rewind_key + @string/notification_action_smart_rewind_prev_key + @string/notification_action_close_key + @string/notification_action_n_a_key - notification_slot_4_key - @string/notification_slot_close_key - - Close - Repeat - Shuffle - Next - Forward - Forward / Next - N/A + notification_action_4_key + @string/notification_action_close_key + + @string/notification_action_close_value + @string/notification_action_repeat_value + @string/notification_action_shuffle_value + @string/notification_action_next_value + @string/notification_action_forward_value + @string/notification_action_smart_forward_next_value + @string/notification_action_n_a_value - - @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 + + @string/notification_action_close_key + @string/notification_action_repeat_key + @string/notification_action_shuffle_key + @string/notification_action_next_key + @string/notification_action_forward_key + @string/notification_action_smart_forward_next_key + @string/notification_action_n_a_key video_mp4 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 20b6b81f6..3f58d186a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,15 +58,30 @@ org.xbmc.kore Show \"Play with Kodi\" option Display an option to play a video via Kodi media center - 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 + + Scale image to 1:1 aspect ratio + This will scale the notification image from 16:9 to 1:1 aspect ratio + First action button + Second action button + Third action button + Fourth action button + Fifth action button + Notification compact view + Notification slots to show in compact view + + Previous + Next + Rewind + Forward + Rewind / Previous + Forward / Next + Play / Pause / Buffering + Play / Pause + Repeat + Shuffle + Close + Nothing + Audio Default audio format Default video format diff --git a/app/src/main/res/xml/appearance_settings.xml b/app/src/main/res/xml/appearance_settings.xml index 3cdaf8e9e..7b25a4ab5 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -42,60 +42,60 @@ From 8b3a09306bb65625b07b955f1312d3486203a167 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 3 Sep 2020 21:49:21 +0200 Subject: [PATCH 10/30] Various notification code improvements Improve builder parameters Reorder code and extract large icon function service.startForeground() now is also provided with service type in android versions >= Q --- .../newpipe/player/NotificationUtil.java | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 96ade4d7e..7c5854442 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -5,8 +5,10 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; +import android.content.pm.ServiceInfo; import android.graphics.Bitmap; import android.graphics.Matrix; +import android.os.Build; import android.util.Log; import androidx.annotation.DrawableRes; @@ -69,38 +71,7 @@ public final class NotificationUtil { ///////////////////////////////////////////////////// /** - * Updates the notification, and the button icons depending on the playback state. - * On old notifications used for changes on the remoteView - * - * @param player the player currently open, to take data from - */ - synchronized void updateNotification(final VideoPlayerImpl player) { - if (DEBUG) { - Log.d(TAG, "N_ updateNotification()"); - } - - if (notificationBuilder == null) { - return; - } - - notificationBuilder.setContentTitle(player.getVideoTitle()); - notificationBuilder.setContentText(player.getUploaderName()); - final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( - player.context.getString(R.string.scale_to_square_image_in_notifications_key), - false); - if (scaleImageToSquareAspectRatio) { - notificationBuilder.setLargeIcon( - getBitmapWithSquareAspectRatio(player.getThumbnail())); - } else { - notificationBuilder.setLargeIcon(player.getThumbnail()); - } - updateActions(notificationBuilder, player); - - notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); - } - - /** - * Creates the notification, if it does not exist already or unless forceRecreate is true. + * Creates the notification if it does not exist already or unless forceRecreate is true. * @param player the player currently open, to take data from * @param forceRecreate whether to force the recreation of the notification even if it already * exists @@ -145,40 +116,57 @@ public final class NotificationUtil { e.printStackTrace(); } + builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle() - .setMediaSession(player.mediaSessionManager.getSessionToken()) - .setShowCancelButton(false) - .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) - .setOngoing(false) - .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, - getIntentForNotification(player), FLAG_UPDATE_CURRENT)) + .setMediaSession(player.mediaSessionManager.getSessionToken()) + .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) + .setPriority(NotificationCompat.PRIORITY_HIGH) .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setContentTitle(player.getVideoTitle()) .setContentText(player.getUploaderName()) - .setDeleteIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, - new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)) .setColor(ContextCompat.getColor(player.context, R.color.gray)) - .setPriority(NotificationCompat.PRIORITY_HIGH); - - final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( - player.context.getString(R.string.scale_to_square_image_in_notifications_key), - false); - if (scaleImageToSquareAspectRatio) { - builder.setLargeIcon(getBitmapWithSquareAspectRatio(player.getThumbnail())); - } else { - builder.setLargeIcon(player.getThumbnail()); - } + .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, + getIntentForNotification(player), FLAG_UPDATE_CURRENT)) + .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, + new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); initializeNotificationSlots(player); updateActions(builder, player); + setLargeIcon(builder, player); return builder; } + /** + * Updates the notification and the button icons depending on the playback state. + * @param player the player currently open, to take data from + */ + synchronized void updateNotification(final VideoPlayerImpl player) { + if (DEBUG) { + Log.d(TAG, "N_ updateNotification()"); + } + + if (notificationBuilder == null) { + return; + } + + notificationBuilder.setContentTitle(player.getVideoTitle()); + notificationBuilder.setContentText(player.getUploaderName()); + updateActions(notificationBuilder, player); + setLargeIcon(notificationBuilder, player); + + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); + } + void startForegroundServiceWithNotification(final Service service) { - service.startForeground(NOTIFICATION_ID, notificationBuilder.build()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + service.startForeground(NOTIFICATION_ID, notificationBuilder.build(), + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); + } else { + service.startForeground(NOTIFICATION_ID, notificationBuilder.build()); + } } @@ -364,6 +352,18 @@ public final class NotificationUtil { // BITMAP ///////////////////////////////////////////////////// + private void setLargeIcon(final NotificationCompat.Builder builder, + final VideoPlayerImpl player) { + final boolean scaleImageToSquareAspectRatio = player.sharedPreferences.getBoolean( + player.context.getString(R.string.scale_to_square_image_in_notifications_key), + false); + if (scaleImageToSquareAspectRatio) { + builder.setLargeIcon(getBitmapWithSquareAspectRatio(player.getThumbnail())); + } else { + builder.setLargeIcon(player.getThumbnail()); + } + } + private Bitmap getBitmapWithSquareAspectRatio(final Bitmap bitmap) { return getResizedBitmap(bitmap, bitmap.getWidth(), bitmap.getWidth()); } From 628575dc5f35ed6f6675223ca79493a199bb3f03 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 3 Sep 2020 22:04:58 +0200 Subject: [PATCH 11/30] Clean up MediaSessionManager --- .../newpipe/player/NotificationUtil.java | 6 ++--- .../player/helper/MediaSessionManager.java | 27 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 7c5854442..95c604472 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -112,7 +112,7 @@ public final class NotificationUtil { compactSlot2 = 2; } } - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } @@ -184,8 +184,8 @@ public final class NotificationUtil { notificationManager.cancel(NOTIFICATION_ID); notificationManager = null; } - } catch (Exception e) { - Log.e("NotificationUtil", "Exception", e); + } catch (final Exception e) { + Log.e(TAG, "Could not cancel notification", e); } } 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 3bb2965a2..8d089c6ed 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 @@ -29,10 +29,8 @@ public class MediaSessionManager { private final MediaSessionCompat mediaSession; @NonNull private final MediaSessionConnector sessionConnector; - @NonNull - private final PlaybackStateCompat.Builder playbackStateCompatBuilder; - private int tmpThumbHash; + private int lastAlbumArtHashCode; public MediaSessionManager(@NonNull final Context context, @NonNull final Player player, @@ -42,15 +40,16 @@ public class MediaSessionManager { | 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.mediaSession.setPlaybackState(new PlaybackStateCompat.Builder() + .setState(PlaybackStateCompat.STATE_NONE, -1, 1) + .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) + .build()); this.sessionConnector = new MediaSessionConnector(mediaSession); this.sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback)); @@ -91,7 +90,7 @@ public class MediaSessionManager { if (getMetadataAlbumArt() == null || getMetadataTitle() == null || getMetadataArtist() == null || getMetadataDuration() <= 1 - || albumArt.hashCode() != tmpThumbHash) { + || albumArt.hashCode() != lastAlbumArtHashCode) { if (DEBUG) { Log.d(TAG, "setMetadata: N_Metadata update: t: " + title + " a: " + artist + " thumb: " + albumArt.hashCode() + " d: " + duration); @@ -103,7 +102,7 @@ public class MediaSessionManager { .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) .putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, albumArt) .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration).build()); - tmpThumbHash = albumArt.hashCode(); + lastAlbumArtHashCode = albumArt.hashCode(); } } From 408e819d321951a060dbafafa7dbfd94295bf4dd Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 7 Sep 2020 15:25:26 +0200 Subject: [PATCH 12/30] Extract duplicate setActivityTitle code to own function --- .../settings/BasePreferenceFragment.java | 22 ++++++------------- .../PeertubeInstanceListFragment.java | 14 ++---------- .../settings/tabs/ChooseTabsFragment.java | 14 ++---------- .../org/schabi/newpipe/util/ThemeHelper.java | 20 +++++++++++++++++ 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java index d8b686297..911d27403 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java @@ -5,12 +5,12 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; import androidx.preference.PreferenceFragmentCompat; import org.schabi.newpipe.MainActivity; +import org.schabi.newpipe.util.ThemeHelper; public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); @@ -25,24 +25,16 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { } @Override - public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + public void onViewCreated(@NonNull final View rootView, + @Nullable final Bundle savedInstanceState) { + super.onViewCreated(rootView, savedInstanceState); setDivider(null); - updateTitle(); + ThemeHelper.setTitleToAppCompatActivity(getActivity(), getPreferenceScreen().getTitle()); } @Override public void onResume() { super.onResume(); - updateTitle(); - } - - private void updateTitle() { - if (getActivity() instanceof AppCompatActivity) { - final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(getPreferenceScreen().getTitle()); - } - } + ThemeHelper.setTitleToAppCompatActivity(getActivity(), getPreferenceScreen().getTitle()); } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java index dfa975eef..5dddb14fd 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java @@ -22,9 +22,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatImageView; import androidx.fragment.app.Fragment; @@ -117,7 +115,8 @@ public class PeertubeInstanceListFragment extends Fragment { @Override public void onResume() { super.onResume(); - updateTitle(); + ThemeHelper.setTitleToAppCompatActivity(getActivity(), + getString(R.string.peertube_instance_url_title)); } @Override @@ -176,15 +175,6 @@ public class PeertubeInstanceListFragment extends Fragment { sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply(); } - private void updateTitle() { - if (getActivity() instanceof AppCompatActivity) { - final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.peertube_instance_url_title); - } - } - } - private void saveChanges() { final JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances"); for (final PeertubeInstance instance : instanceList) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index 44fe987ee..2554ecc5c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -16,9 +16,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatImageView; import androidx.fragment.app.Fragment; @@ -92,7 +90,8 @@ public class ChooseTabsFragment extends Fragment { @Override public void onResume() { super.onResume(); - updateTitle(); + ThemeHelper.setTitleToAppCompatActivity(getActivity(), + getString(R.string.main_page_content)); } @Override @@ -137,15 +136,6 @@ public class ChooseTabsFragment extends Fragment { tabList.addAll(tabsManager.getTabs()); } - private void updateTitle() { - if (getActivity() instanceof AppCompatActivity) { - final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.main_page_content); - } - } - } - private void saveChanges() { tabsManager.saveTabs(tabList); } diff --git a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java index 88e1a31cd..6b8e884a2 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java @@ -19,6 +19,7 @@ package org.schabi.newpipe.util; +import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.preference.PreferenceManager; @@ -26,7 +27,10 @@ import android.util.TypedValue; import android.view.ContextThemeWrapper; import androidx.annotation.AttrRes; +import androidx.annotation.Nullable; import androidx.annotation.StyleRes; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import org.schabi.newpipe.R; @@ -231,4 +235,20 @@ public final class ThemeHelper { return PreferenceManager.getDefaultSharedPreferences(context) .getString(themeKey, defaultTheme); } + + /** + * Sets the title to the activity, if the activity is an {@link AppCompatActivity} and has an + * action bar. + * @param activity the activity to set the title of + * @param title the title to set to the activity + */ + public static void setTitleToAppCompatActivity(@Nullable final Activity activity, + final CharSequence title) { + if (activity instanceof AppCompatActivity) { + final ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(title); + } + } + } } From 71b32fe64104eabd422b7aa4cea6b30e53c48d22 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 8 Sep 2020 19:02:05 +0200 Subject: [PATCH 13/30] Add notification costumization settings menu --- .../org/schabi/newpipe/RouterActivity.java | 2 +- .../org/schabi/newpipe/player/MainPlayer.java | 8 +- .../newpipe/player/NotificationConstants.java | 141 +++++++++ .../newpipe/player/NotificationUtil.java | 189 ++++++------ .../newpipe/player/VideoPlayerImpl.java | 5 + .../NotificationSettingsFragment.java | 271 ++++++++++++++++++ .../drawable-hdpi/ic_close_white_24dp_png.png | Bin 0 -> 415 bytes ...ng => ic_hourglass_top_white_24dp_png.png} | Bin .../drawable-mdpi/ic_close_white_24dp_png.png | Bin 0 -> 285 bytes ...ng => ic_hourglass_top_white_24dp_png.png} | Bin .../ic_close_white_24dp_png.png | Bin 0 -> 602 bytes ...ng => ic_hourglass_top_white_24dp_png.png} | Bin .../ic_close_white_24dp_png.png | Bin 0 -> 1034 bytes ...ng => ic_hourglass_top_white_24dp_png.png} | Bin .../ic_close_white_24dp_png.png | Bin 0 -> 2068 bytes ...ng => ic_hourglass_top_white_24dp_png.png} | Bin .../drawable/ic_hourglass_top_white_24dp.xml | 9 + .../main/res/layout/list_radio_icon_item.xml | 4 +- .../main/res/layout/settings_notification.xml | 135 +++++++++ .../layout/settings_notification_action.xml | 88 ++++++ ...view.xml => single_choice_dialog_view.xml} | 0 app/src/main/res/values/settings_keys.xml | 109 +------ app/src/main/res/values/strings.xml | 31 +- app/src/main/res/xml/appearance_settings.xml | 66 ----- app/src/main/res/xml/main_settings.xml | 6 + 25 files changed, 774 insertions(+), 290 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java create mode 100644 app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java create mode 100644 app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png rename app/src/main/res/drawable-hdpi/{ic_hourglass_top_white_24dp.png => ic_hourglass_top_white_24dp_png.png} (100%) create mode 100644 app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png rename app/src/main/res/drawable-mdpi/{ic_hourglass_top_white_24dp.png => ic_hourglass_top_white_24dp_png.png} (100%) create mode 100644 app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png rename app/src/main/res/drawable-xhdpi/{ic_hourglass_top_white_24dp.png => ic_hourglass_top_white_24dp_png.png} (100%) create mode 100644 app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png rename app/src/main/res/drawable-xxhdpi/{ic_hourglass_top_white_24dp.png => ic_hourglass_top_white_24dp_png.png} (100%) create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png rename app/src/main/res/drawable-xxxhdpi/{ic_hourglass_top_white_24dp.png => ic_hourglass_top_white_24dp_png.png} (100%) create mode 100644 app/src/main/res/drawable/ic_hourglass_top_white_24dp.xml create mode 100644 app/src/main/res/layout/settings_notification.xml create mode 100644 app/src/main/res/layout/settings_notification_action.xml rename app/src/main/res/layout/{preferred_player_dialog_view.xml => single_choice_dialog_view.xml} (100%) diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 70fceaf07..0d658f3a1 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -258,7 +258,7 @@ public class RouterActivity extends AppCompatActivity { final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext); final LinearLayout rootLayout = (LinearLayout) inflater.inflate( - R.layout.preferred_player_dialog_view, null, false); + R.layout.single_choice_dialog_view, null, false); final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list); final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> { diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index 065efca02..a16fc3cbf 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -77,11 +77,11 @@ public final class MainPlayer extends Service { static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD"; static final String ACTION_BUFFERING - = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_BUFFERING"; + = "org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING"; static final String ACTION_SHUFFLE - = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_SHUFFLE"; - - static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource"; + = "org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE"; + public static final String ACTION_RECREATE_NOTIFICATION + = "org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION"; /*////////////////////////////////////////////////////////////////////////// // Service's LifeCycle diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java b/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java new file mode 100644 index 000000000..599e18e65 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java @@ -0,0 +1,141 @@ +package org.schabi.newpipe.player; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.DrawableRes; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import org.schabi.newpipe.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +public final class NotificationConstants { + + private NotificationConstants() { } + + + public static final int NOTHING = 0; + public static final int PREVIOUS = 1; + public static final int NEXT = 2; + public static final int REWIND = 3; + public static final int FORWARD = 4; + public static final int SMART_REWIND_PREVIOUS = 5; + public static final int SMART_FORWARD_NEXT = 6; + public static final int PLAY_PAUSE = 7; + public static final int PLAY_PAUSE_BUFFERING = 8; + public static final int REPEAT = 9; + public static final int SHUFFLE = 10; + public static final int CLOSE = 11; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, + PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE}) + public @interface Action { } + + @StringRes + public static final int[] ACTION_SUMMARIES = { + R.string.notification_action_nothing, + R.string.notification_action_previous, + R.string.notification_action_next, + R.string.notification_action_rewind, + R.string.notification_action_forward, + R.string.notification_action_smart_rewind_previous, + R.string.notification_action_smart_forward_next, + R.string.notification_action_play_pause, + R.string.notification_action_play_pause_buffering, + R.string.notification_action_repeat, + R.string.notification_action_shuffle, + R.string.close, + }; + + @DrawableRes + public static final int[] ACTION_ICONS = { + 0, + R.drawable.exo_icon_previous, + R.drawable.exo_icon_next, + R.drawable.exo_icon_rewind, + R.drawable.exo_icon_fastforward, + R.drawable.exo_icon_previous, + R.drawable.exo_icon_next, + R.drawable.ic_pause_white_24dp, + R.drawable.ic_hourglass_top_white_24dp, + R.drawable.exo_icon_repeat_all, + R.drawable.exo_icon_shuffle_on, + R.drawable.ic_close_white_24dp, + }; + + + @Action + public static final int[] SLOT_DEFAULTS = { + SMART_REWIND_PREVIOUS, + PLAY_PAUSE_BUFFERING, + SMART_FORWARD_NEXT, + REPEAT, + CLOSE, + }; + + @Action + public static final int[][] SLOT_ALLOWED_ACTIONS = { + new int[] {PREVIOUS, REWIND, SMART_REWIND_PREVIOUS}, + new int[] {REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, + new int[] {NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, + new int[] {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, + SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, + new int[] {NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, + }; + + public static final int[] SLOT_PREF_KEYS = { + R.string.notification_slot_0_key, + R.string.notification_slot_1_key, + R.string.notification_slot_2_key, + R.string.notification_slot_3_key, + R.string.notification_slot_4_key, + }; + + + public static final Integer[] SLOT_COMPACT_DEFAULTS = {0, 1, 2}; + + public static final int[] SLOT_COMPACT_PREF_KEYS = { + R.string.notification_slot_compact_0_key, + R.string.notification_slot_compact_1_key, + R.string.notification_slot_compact_2_key, + }; + + /** + * @param context the context to use + * @param sharedPreferences the shared preferences to query values from + * @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make + * it lower if there are slots with empty actions) + * @return a sorted list of the indices of the slots to use as compact slots + */ + public static List getCompactSlotsFromPreferences( + @NonNull final Context context, + final SharedPreferences sharedPreferences, + final int slotCount) { + final SortedSet compactSlots = new TreeSet<>(); + for (int i = 0; i < 3; i++) { + final int compactSlot = sharedPreferences.getInt( + context.getString(SLOT_COMPACT_PREF_KEYS[i]), Integer.MAX_VALUE); + + if (compactSlot == Integer.MAX_VALUE) { + // settings not yet populated, return default values + return new ArrayList<>(Arrays.asList(SLOT_COMPACT_DEFAULTS)); + } + + // a negative value (-1) is set when the user does not want a particular compact slot + if (compactSlot >= 0 && compactSlot < slotCount) { + compactSlots.add(compactSlot); + } + } + return new ArrayList<>(compactSlots); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 95c604472..2c5e882bb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -20,6 +20,8 @@ import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.util.NavigationHelper; +import java.util.List; + import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.content.Context.NOTIFICATION_SERVICE; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; @@ -46,11 +48,8 @@ public final class NotificationUtil { @Nullable private static NotificationUtil instance = null; - private String notificationSlot0 = "smart_rewind_prev"; - private String notificationSlot1 = "play_pause_buffering"; - private String notificationSlot2 = "smart_forward_next"; - private String notificationSlot3 = "repeat"; - private String notificationSlot4 = "close"; + @NotificationConstants.Action + private int[] notificationSlots = NotificationConstants.SLOT_DEFAULTS.clone(); private NotificationManager notificationManager; private NotificationCompat.Builder notificationBuilder; @@ -91,35 +90,28 @@ public final class NotificationUtil { final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, player.context.getString(R.string.notification_channel_id)); - final String compactView = player.sharedPreferences.getString(player.context.getString( - R.string.settings_notifications_compact_view_key), "0,1,2"); - int compactSlot0 = 0; - int compactSlot1 = 1; - int compactSlot2 = 2; - try { - if (compactView != null) { - final 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 (final Exception e) { - e.printStackTrace(); + initializeNotificationSlots(player); + + // count the number of real slots, to make sure compact slots indices are not out of bound + int nonNothingSlotCount = 5; + if (notificationSlots[3] == NotificationConstants.NOTHING) { + --nonNothingSlotCount; + } + if (notificationSlots[4] == NotificationConstants.NOTHING) { + --nonNothingSlotCount; } + // build the compact slot indices array (need code to convert from Integer... because Java) + final List compactSlotList = NotificationConstants.getCompactSlotsFromPreferences( + player.context, player.sharedPreferences, nonNothingSlotCount); + final int[] compactSlots = new int[compactSlotList.size()]; + for (int i = 0; i < compactSlotList.size(); i++) { + compactSlots[i] = compactSlotList.get(i); + } builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle() .setMediaSession(player.mediaSessionManager.getSessionToken()) - .setShowActionsInCompactView(compactSlot0, compactSlot1, compactSlot2)) + .setShowActionsInCompactView(compactSlots)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) @@ -131,7 +123,6 @@ public final class NotificationUtil { .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); - initializeNotificationSlots(player); updateActions(builder, player); setLargeIcon(builder, player); @@ -171,22 +162,20 @@ public final class NotificationUtil { boolean hasSlotWithBuffering() { - return notificationSlot0.equals("play_pause_buffering") - || notificationSlot1.equals("play_pause_buffering") - || notificationSlot2.equals("play_pause_buffering") - || notificationSlot3.equals("play_pause_buffering") - || notificationSlot4.equals("play_pause_buffering"); + return notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING + || notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING; } - public void cancelNotification() { + void cancelNotification() { try { if (notificationManager != null) { notificationManager.cancel(NOTIFICATION_ID); - notificationManager = null; } } catch (final Exception e) { Log.e(TAG, "Could not cancel notification", e); } + notificationManager = null; + notificationBuilder = null; } @@ -195,32 +184,25 @@ public final class NotificationUtil { ///////////////////////////////////////////////////// private void initializeNotificationSlots(final VideoPlayerImpl player) { - notificationSlot0 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_action_0_key), notificationSlot0); - notificationSlot1 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_action_1_key), notificationSlot1); - notificationSlot2 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_action_2_key), notificationSlot2); - notificationSlot3 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_action_3_key), notificationSlot3); - notificationSlot4 = player.sharedPreferences.getString( - player.context.getString(R.string.notification_action_4_key), notificationSlot4); + for (int i = 0; i < 5; ++i) { + notificationSlots[i] = player.sharedPreferences.getInt( + player.context.getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i]); + } } @SuppressLint("RestrictedApi") private void updateActions(final NotificationCompat.Builder builder, final VideoPlayerImpl player) { builder.mActions.clear(); - addAction(builder, player, notificationSlot0); - addAction(builder, player, notificationSlot1); - addAction(builder, player, notificationSlot2); - addAction(builder, player, notificationSlot3); - addAction(builder, player, notificationSlot4); + for (int i = 0; i < 5; ++i) { + addAction(builder, player, notificationSlots[i]); + } } private void addAction(final NotificationCompat.Builder builder, final VideoPlayerImpl player, - final String slot) { + @NotificationConstants.Action final int slot) { final NotificationCompat.Action action = getAction(player, slot); if (action != null) { builder.addAction(action); @@ -228,23 +210,42 @@ public final class NotificationUtil { } @Nullable - private NotificationCompat.Action getAction(final VideoPlayerImpl player, - final String slot) { - switch (slot) { - case "play_pause_buffering": - if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT - || player.getCurrentState() == BasePlayer.STATE_BLOCKED - || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { - return getAction(player, R.drawable.ic_hourglass_top_white_24dp, - "Buffering", ACTION_BUFFERING); + private NotificationCompat.Action getAction( + final VideoPlayerImpl player, + @NotificationConstants.Action final int selectedAction) { + final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction]; + switch (selectedAction) { + case NotificationConstants.PREVIOUS: + return getAction(player, baseActionIcon, "Previous", ACTION_PLAY_PREVIOUS); + + case NotificationConstants.NEXT: + return getAction(player, baseActionIcon, "Next", ACTION_PLAY_NEXT); + + case NotificationConstants.REWIND: + return getAction(player, baseActionIcon, "Rewind", ACTION_FAST_REWIND); + + case NotificationConstants.FORWARD: + return getAction(player, baseActionIcon, "Forward", ACTION_FAST_FORWARD); + + case NotificationConstants.SMART_REWIND_PREVIOUS: + if (player.playQueue != null && player.playQueue.size() > 1) { + return getAction(player, R.drawable.exo_notification_previous, + "Previous", ACTION_PLAY_PREVIOUS); } else { - return getAction(player, - player.isPlaying() ? R.drawable.exo_notification_pause - : R.drawable.exo_notification_play, - player.isPlaying() ? "Pause" : "Play", - ACTION_PLAY_PAUSE); + return getAction(player, R.drawable.exo_controls_rewind, + "Rewind", ACTION_FAST_REWIND); } - case "play_pause": + + case NotificationConstants.SMART_FORWARD_NEXT: + if (player.playQueue != null && player.playQueue.size() > 1) { + return getAction(player, R.drawable.exo_notification_next, + "Next", ACTION_PLAY_NEXT); + } else { + return getAction(player, R.drawable.exo_controls_fastforward, + "Forward", ACTION_FAST_FORWARD); + } + + case NotificationConstants.PLAY_PAUSE: final boolean pauseOrPlay = player.isPlaying() || player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED @@ -254,48 +255,38 @@ public final class NotificationUtil { : R.drawable.exo_notification_play, pauseOrPlay ? "Pause" : "Play", ACTION_PLAY_PAUSE); - case "rewind": - return getAction(player, R.drawable.exo_controls_rewind, - "Rewind", ACTION_FAST_REWIND); - case "smart_rewind_prev": - if (player.playQueue != null && player.playQueue.size() > 1) { - return getAction(player, R.drawable.exo_notification_previous, - "Prev", ACTION_PLAY_PREVIOUS); + + case NotificationConstants.PLAY_PAUSE_BUFFERING: + if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || player.getCurrentState() == BasePlayer.STATE_BLOCKED + || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { + return getAction(player, R.drawable.ic_hourglass_top_white_24dp_png, + "Buffering", ACTION_BUFFERING); } else { - return getAction(player, R.drawable.exo_controls_rewind, - "Rewind", ACTION_FAST_REWIND); + return getAction(player, + player.isPlaying() ? R.drawable.exo_notification_pause + : R.drawable.exo_notification_play, + player.isPlaying() ? "Pause" : "Play", + ACTION_PLAY_PAUSE); } - case "forward": - return getAction(player, R.drawable.exo_controls_fastforward, - "Forward", ACTION_FAST_FORWARD); - case "smart_forward_next": - if (player.playQueue != null && player.playQueue.size() > 1) { - return getAction(player, R.drawable.exo_notification_next, - "Next", ACTION_PLAY_NEXT); - } else { - return getAction(player, R.drawable.exo_controls_fastforward, - "Forward", ACTION_FAST_FORWARD); - } - case "next": - return getAction(player, R.drawable.exo_notification_next, - "Next", ACTION_PLAY_NEXT); - case "prev": - return getAction(player, R.drawable.exo_notification_previous, - "Prev", ACTION_PLAY_PREVIOUS); - case "repeat": + + case NotificationConstants.REPEAT: return getAction(player, getRepeatModeDrawable(player.getRepeatMode()), getRepeatModeTitle(player.getRepeatMode()), ACTION_REPEAT); - case "shuffle": + + case NotificationConstants.SHUFFLE: final boolean shuffled = player.playQueue != null && player.playQueue.isShuffled(); return getAction(player, shuffled ? R.drawable.exo_controls_shuffle_on : R.drawable.exo_controls_shuffle_off, shuffled ? "ShuffleOn" : "ShuffleOff", ACTION_SHUFFLE); - case "close": - return getAction(player, R.drawable.ic_close_white_24dp, + + case NotificationConstants.CLOSE: + return getAction(player, R.drawable.ic_close_white_24dp_png, "Close", ACTION_CLOSE); - case "n/a": + + case NotificationConstants.NOTHING: default: // do nothing return null; diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 024be26dc..c1171efb7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -112,6 +112,7 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_OPEN_CONTROLS; import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT; import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PAUSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS; +import static org.schabi.newpipe.player.MainPlayer.ACTION_RECREATE_NOTIFICATION; import static org.schabi.newpipe.player.MainPlayer.ACTION_REPEAT; import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE; import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND; @@ -1179,6 +1180,7 @@ public class VideoPlayerImpl extends VideoPlayer intentFilter.addAction(ACTION_FAST_FORWARD); intentFilter.addAction(ACTION_BUFFERING); intentFilter.addAction(ACTION_SHUFFLE); + intentFilter.addAction(ACTION_RECREATE_NOTIFICATION); intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED); intentFilter.addAction(VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED); @@ -1236,6 +1238,9 @@ public class VideoPlayerImpl extends VideoPlayer case ACTION_SHUFFLE: onShuffleClicked(); break; + case ACTION_RECREATE_NOTIFICATION: + resetNotification(true); + break; case Intent.ACTION_HEADSET_PLUG: //FIXME /*notificationManager.cancel(NOTIFICATION_ID); mediaSessionManager.dispose(); diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java new file mode 100644 index 000000000..f4bbc96a7 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java @@ -0,0 +1,271 @@ +package org.schabi.newpipe.settings; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.fragment.app.Fragment; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.player.MainPlayer; +import org.schabi.newpipe.player.NotificationConstants; +import org.schabi.newpipe.util.DeviceUtils; +import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.views.FocusOverlayView; + +import java.util.List; + +public class NotificationSettingsFragment extends Fragment { + + private Switch scaleSwitch; + private NotificationSlot[] notificationSlots; + + private SharedPreferences pref; + private List compactSlots; + private String scaleKey; + + //////////////////////////////////////////////////////////////////////////// + // Lifecycle + //////////////////////////////////////////////////////////////////////////// + + @Override + public void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + pref = PreferenceManager.getDefaultSharedPreferences(requireContext()); + scaleKey = getString(R.string.scale_to_square_image_in_notifications_key); + } + + @Override + public View onCreateView(@NonNull final LayoutInflater inflater, + final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + return inflater.inflate(R.layout.settings_notification, container, false); + } + + @Override + public void onViewCreated(@NonNull final View rootView, + @Nullable final Bundle savedInstanceState) { + super.onViewCreated(rootView, savedInstanceState); + + setupScaleSwitch(rootView); + setupActions(rootView); + } + + @Override + public void onResume() { + super.onResume(); + ThemeHelper.setTitleToAppCompatActivity(getActivity(), + getString(R.string.settings_category_notification_title)); + } + + @Override + public void onPause() { + super.onPause(); + saveChanges(); + requireContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION)); + } + + + //////////////////////////////////////////////////////////////////////////// + // Setup + //////////////////////////////////////////////////////////////////////////// + + private void setupScaleSwitch(@NonNull final View view) { + scaleSwitch = view.findViewById(R.id.notificationScaleSwitch); + scaleSwitch.setChecked(pref.getBoolean(scaleKey, false)); + + view.findViewById(R.id.notificationScaleSwitchClickableArea) + .setOnClickListener(v -> scaleSwitch.toggle()); + } + + private void setupActions(@NonNull final View view) { + compactSlots = + NotificationConstants.getCompactSlotsFromPreferences(requireContext(), pref, 5); + notificationSlots = new NotificationSlot[5]; + for (int i = 0; i < 5; i++) { + notificationSlots[i] = new NotificationSlot(i, view); + } + } + + + //////////////////////////////////////////////////////////////////////////// + // Saving + //////////////////////////////////////////////////////////////////////////// + + private void saveChanges() { + final SharedPreferences.Editor editor = pref.edit(); + editor.putBoolean(scaleKey, scaleSwitch.isChecked()); + + for (int i = 0; i < 3; i++) { + editor.putInt(getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), + (i < compactSlots.size() ? compactSlots.get(i) : -1)); + } + + for (int i = 0; i < 5; i++) { + editor.putInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + notificationSlots[i].selectedAction); + } + + editor.apply(); + } + + + //////////////////////////////////////////////////////////////////////////// + // Notification action + //////////////////////////////////////////////////////////////////////////// + + private static final int[] SLOT_ITEMS = { + R.id.notificationAction0, + R.id.notificationAction1, + R.id.notificationAction2, + R.id.notificationAction3, + R.id.notificationAction4, + }; + + private static final int[] SLOT_TITLES = { + R.string.notification_action_0_title, + R.string.notification_action_1_title, + R.string.notification_action_2_title, + R.string.notification_action_3_title, + R.string.notification_action_4_title, + }; + + private class NotificationSlot { + + final int i; + @NotificationConstants.Action int selectedAction; + + ImageView icon; + TextView summary; + + NotificationSlot(final int actionIndex, final View parentView) { + this.i = actionIndex; + + final View view = parentView.findViewById(SLOT_ITEMS[i]); + setupSelectedAction(view); + setupTitle(view); + setupCheckbox(view); + } + + void setupTitle(final View view) { + ((TextView) view.findViewById(R.id.notificationActionTitle)) + .setText(SLOT_TITLES[i]); + view.findViewById(R.id.notificationActionClickableArea).setOnClickListener( + v -> openActionChooserDialog()); + } + + void setupCheckbox(final View view) { + final CheckBox compactSlotCheckBox = view.findViewById(R.id.notificationActionCheckBox); + compactSlotCheckBox.setChecked(compactSlots.contains(i)); + view.findViewById(R.id.notificationActionCheckBoxClickableArea).setOnClickListener( + v -> { + if (compactSlotCheckBox.isChecked()) { + compactSlots.remove((Integer) i); + } else if (compactSlots.size() < 3) { + compactSlots.add(i); + } else { + Toast.makeText(requireContext(), + R.string.notification_actions_at_most_three, + Toast.LENGTH_SHORT).show(); + return; + } + + compactSlotCheckBox.toggle(); + }); + } + + void setupSelectedAction(final View view) { + icon = view.findViewById(R.id.notificationActionIcon); + summary = view.findViewById(R.id.notificationActionSummary); + selectedAction = pref.getInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + NotificationConstants.SLOT_DEFAULTS[i]); + updateInfo(); + } + + void updateInfo() { + if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) { + icon.setImageDrawable(null); + } else { + icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), + NotificationConstants.ACTION_ICONS[selectedAction])); + } + + summary.setText(NotificationConstants.ACTION_SUMMARIES[selectedAction]); + } + + void openActionChooserDialog() { + final LayoutInflater inflater = LayoutInflater.from(requireContext()); + final LinearLayout rootLayout = (LinearLayout) inflater.inflate( + R.layout.single_choice_dialog_view, null, false); + final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list); + + final AlertDialog alertDialog = new AlertDialog.Builder(requireContext()) + .setTitle(SLOT_TITLES[i]) + .setView(radioGroup) + .setCancelable(true) + .create(); + + final View.OnClickListener radioButtonsClickListener = v -> { + final int id = ((RadioButton) v).getId(); + selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id]; + updateInfo(); + alertDialog.dismiss(); + }; + + for (int id = 0; id < NotificationConstants.SLOT_ALLOWED_ACTIONS[i].length; ++id) { + final int action = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id]; + final RadioButton radioButton + = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null); + + // if present set action icon with correct color + if (NotificationConstants.ACTION_ICONS[action] != 0) { + final Drawable drawable = AppCompatResources.getDrawable(requireContext(), + NotificationConstants.ACTION_ICONS[action]); + if (drawable != null) { + final int color = ThemeHelper.resolveColorFromAttr(requireContext(), + android.R.attr.textColorPrimary); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable.setTint(color); + } else { + drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + radioButton.setCompoundDrawablesWithIntrinsicBounds( + null, null, drawable, null); + } + } + + radioButton.setText(NotificationConstants.ACTION_SUMMARIES[action]); + radioButton.setChecked(action == selectedAction); + radioButton.setId(id); + radioButton.setLayoutParams(new RadioGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + radioButton.setOnClickListener(radioButtonsClickListener); + radioGroup.addView(radioButton); + } + alertDialog.show(); + + if (DeviceUtils.isTv(requireContext())) { + FocusOverlayView.setupFocusObserver(alertDialog); + } + } + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..2f73a04b16de2d72f6dde7b999d52c196a3d77ed GIT binary patch literal 415 zcmV;Q0bu@#P))B%|VoRJ~51*73|4$uU7wV=LfQ}Z6z>*ua= zz!a=!$bizDY7D3=LCrcgQ_)sin3`_iNWwV%t4_3`c2fu2KEo#SHr)s=ll~i65tRL) z|JmoTsohM)ks)MLjmtidO||%D0_>BqNzgtyn}qC>w~5hB=56xY_Xrr5O-}nB0pzo( zk?6iq7;iU8bYCdUzugqueVom7b`!VzVr}Z|roP=LWmB(i>gqm8o3em23Nia|(N3b9 zJoarlK<~&`Y&SXV(=QGs2WU!AyV!0r?Az1Z%s9Phrb+Xx`32$efF+z^MPdK|002ov JPDHLkV1i~e!B_wQ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp.png rename to app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..d8aa2f7c4728f3b856ceb84edeb60832e79fcf23 GIT binary patch literal 285 zcmV+&0pk9NP)8bGvk?rMKCB{yZl|vD)G+ z##jL@;0!SyI09S7`2@Uy3qzvGufR)47=iTNaY121+M& z#z&Ih)F;r%lLUL11@0g20P(mp)0?=xB5iB0Yp6QBD7W zx1`g!JG;FZJ>=ZD}aQdPT4cF2JX< z=7`eifcqnhf_N9pnt~GwB5(NSrTLa(UeHeT+085IG!TC^rJX1ntPyJ7yrBO*wzP>c zum^56LColzs3d%_y%pwVP@#*aCi8b6+$biZ{MGh}g*rdcm~T;96#^H+gSIFLbtkla zLR06}H!sZ>U5^(pJ|>qJx_21&<3xz z=J}XDA+Is(906Q`A^5O0&ujX`obFV!Z literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp.png rename to app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..2cd1a88659f07386e028e70877802dd38ecb7ecb GIT binary patch literal 1034 zcmV+l1oiugP)o$Qc;3T*Sj)DV%>Kg$kz!h*T4RN}_C*T|K3-}tG2D`z#DGJj87Qi`h3H-#r zAAxyWMYK?ykKn%hAAi6-@G1B(MS+UoGw=YcvbG=Kdn1yqB8*cODb5LtsDQt~E%2og z$)=O&oFbjP4W5D*tnG**%~@$TI_JB49S#fIqLaEU3RIwTw)xMx@TkwU+lq~1=D`iu z8xEbcN2d)~sAq)kIH+;XB`(-*W+qM85a>Dhj@CW%5UIkM^cEZk+rT>s)X{1NFE|6P zQv@$@PAQ%dam1FTAAqmGZ*F~@u{hIi{Rz}C$hN9n8OXKbRM>Xrx`NHSMGrUxE>k2g zC;bJ_dCd#37i^E=y*t4^umqOb)=nMgY;$m%0p5|uZFPuFI`VOnDfBCy)EmQl_R$eL zbch$KY%6nJG0hWe58KVmqz^vD9#JIYr2Z%~i8E)A&N1Q~d(0e%@3+pGQ%J=*bq4o> zkBh2w(lVXY7U4bebVS+bR_^e%8EXSM#0ThNyM6W%>YPH2hdBc9RzAZI@AwSlGDXbi zq`?eeb5iAbW0yjB4em1@0dCB0HQDO&R_-;BkJ)x5wqdK}r2m*s+Vyc#pzKyBTX~(k zc6eQ179NG%qex3W?dF@^ZpcHEIylf>byUEQ~Lb99OY$YVS$1k6Es_e&Th9spfK4gbauN91365#3YFcm zEKH=mm6W}k4S^!>tt9N-k`$)ly;YifHyZ-Qwzo=S@0Rq;iFI#fbMIzDpv3l8Hui35 zXRC(yR!Q#N(#}?~?5&d6yGhAbvF@$n+`CE1Rd7Xh0#ikSu1z(er2guT@b+3kd1dN)6t zn6!0b9_E+H4<+rb>SVWle)-+}e4@oh5zXLzinPXdTa~c4s>yB}{3k9_95c|HY!%W> zC(UvuO-b5Yad6MmIn(?BPE$^yP$(1%g+ifFC={8%Keh)tWYG$(asU7T07*qoM6N<$ Ef)k_jzyJUM literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp.png rename to app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..4d278c5bfba91b997502367af09ec686afaf7584 GIT binary patch literal 2068 zcmV+v2elZDN|DP|FjR$7bbg?m#k)*mJjK_RVG z3=%PQgJxK(7)4QUd{bG0f+Mgu1*N5_znvSOyFD}Y_gqbX_owFz-#6v+yXQXVIro0g zdCq+T0fj=LP$(1%g+ifFC=?2XLZMJ76befOgTWYKtT0=6T399g7|x2&drA~;fCq(# zg-g`|>VS79-eI0_4-?-eY!x=buY?)GIT14PX~HaFiSPz&5k3?i;om>;i?OlLnfw~z z?cjfS0n3Pl^aydlQda$b;T!1U4`HR_1iu)&5}so69~;KLg1-nWg!#e*VZ2W#K1rA& z+{`3j5cWbB%{bu*j#?USqif*b!J*MbB-9WISw3~Z=dAootoq(z?&%@#gu~%Bx*E0* zW9%y&;H;?C!W>~Vj0&^;QO0}Bnfy({CJP6&@M1m~ZU^jy&B4&qbQL=Cb)loV!yq-CoTXTJ2lim& zP!r$FV{|`KG;zG`9`1(L6A5Q6BB9@qsNWI^Y2$3~5>{}V#!doF6@EV>G9zdcxD=J}(?MxP?3QL8@g>^h~hI;I{|JQ^%+Kl{63-}RY*a9Nq z`=A|MiTW#%uzZZ|TgM9i-9qW~8!LYk<5K*dMSdvUCfqLUguNjo>PvEjwN8p}WY7ECL)M_ZC8SlxK*@n{TuuwXE{LLL> zTuh`@64e1d$S{+jBTd7U+^;98GTvzT$Ifr$vYvH7g7 z?}X3c8w-hgo|46NiBfXZ%LF@iOhVkeXGD%Lz>?mP)CL22@a!mmhGMKHIeY!2psSRb>yu2RXUp^b3iWbAJriw=0LpW zC4G`SAxmd-WDZ!x3O-{YH3yTZM@iO}=xlm>2e{AcLZ0CbA*AL|67?Jzvs1R12BUPA zhj+kKL$TbqD5}{M@$MQ$yf~d@b$~A?K7|amz{px{s+xTvN)UZKj#{9zsk6*>dvh{5 zAe~je)v~JD>mfRNjVfv$kuX9$Zh7g0wAM(gd+bu)%eY0Oxb9B+j=V_J14P2YNSB&!Z+$WS_H~w0)9vLiB2P#i zp$(Zhu#678N0t)x3}0{N8I{?nNOM4pp;+#CP&ysPxY_T?mPA7#J@=rHt{Ms{S{YP95&qcMSmB^ua$8S;jFNq2x%ym8y2l2uF`pCj7tc!{Yi#G>a^^* zc!6kmtB}+IliUGKu){*>G#RIkxaYL_;mkPqaFFes@^Z_<5v|mzs>1C6_vEXm77n=N zo;u=FCAW=8xM|^lHckjW9d4s@p(D{AkhBbT%vU86HN~$-zHrpe0wn6$AtdT!&PT06 zJYaqn3?eo6v-16U@b$a*w3!$d{IH88eQHOEr zNeOvET7)=YG5w_FMrF30!)Z@3`FZYp%r8byArf+VCo~yKCn7gC=o@ksQZr4!Dba8$F0000 + + diff --git a/app/src/main/res/layout/list_radio_icon_item.xml b/app/src/main/res/layout/list_radio_icon_item.xml index 947747c16..3446e351c 100644 --- a/app/src/main/res/layout/list_radio_icon_item.xml +++ b/app/src/main/res/layout/list_radio_icon_item.xml @@ -10,10 +10,8 @@ android:maxLines="2" android:minHeight="?attr/listPreferredItemHeightSmall" android:paddingEnd="?attr/listPreferredItemPaddingRight" - android:paddingLeft="?attr/listPreferredItemPaddingLeft" - android:paddingRight="?attr/listPreferredItemPaddingRight" android:paddingStart="?attr/listPreferredItemPaddingLeft" android:background="?attr/checked_selector" android:textColor="?attr/textColorAlertDialogListItem" - tools:drawableLeft="?attr/ic_play" + tools:drawableLeft="?attr/ic_play_arrow" tools:text="Lorem ipsum dolor sit amet" /> diff --git a/app/src/main/res/layout/settings_notification.xml b/app/src/main/res/layout/settings_notification.xml new file mode 100644 index 000000000..61b7f1aad --- /dev/null +++ b/app/src/main/res/layout/settings_notification.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/settings_notification_action.xml b/app/src/main/res/layout/settings_notification_action.xml new file mode 100644 index 000000000..8ef2d8ab2 --- /dev/null +++ b/app/src/main/res/layout/settings_notification_action.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/preferred_player_dialog_view.xml b/app/src/main/res/layout/single_choice_dialog_view.xml similarity index 100% rename from app/src/main/res/layout/preferred_player_dialog_view.xml rename to app/src/main/res/layout/single_choice_dialog_view.xml diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 52b43002d..0aa1435c1 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -118,108 +118,15 @@ 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 + notification_slot_1_key + notification_slot_2_key + notification_slot_3_key + notification_slot_4_key - notification_action_0_key - @string/notification_action_smart_rewind_prev_key - - @string/notification_action_smart_rewind_prev_value - @string/notification_action_prev_value - @string/notification_action_rewind_value - - - @string/notification_action_smart_rewind_prev_key - @string/notification_action_prev_key - @string/notification_action_rewind_key - - - notification_action_1_key - @string/notification_action_play_pause_buffering_key - - @string/notification_action_play_pause_buffering_value - @string/notification_action_play_pause_value - @string/notification_action_rewind_value - - - @string/notification_action_play_pause_buffering_key - @string/notification_action_play_pause_key - @string/notification_action_rewind_key - - - - notification_action_2_key - @string/notification_action_smart_forward_next_key - - @string/notification_action_smart_forward_next_value - @string/notification_action_forward_value - @string/notification_action_next_value - @string/notification_action_play_pause_buffering_value - @string/notification_action_play_pause_value - - - @string/notification_action_smart_forward_next_key - @string/notification_action_forward_key - @string/notification_action_next_key - @string/notification_action_play_pause_buffering_key - @string/notification_action_play_pause_key - - - notification_action_3_key - @string/notification_action_repeat_key - - @string/notification_action_repeat_value - @string/notification_action_shuffle_value - @string/notification_action_prev_value - @string/notification_action_forward_value - @string/notification_action_smart_forward_next_value - @string/notification_action_rewind_value - @string/notification_action_smart_rewind_prev_value - @string/notification_action_close_value - @string/notification_action_n_a_value - - - @string/notification_action_repeat_key - @string/notification_action_shuffle_key - @string/notification_action_prev_key - @string/notification_action_forward_key - @string/notification_action_smart_forward_next_key - @string/notification_action_rewind_key - @string/notification_action_smart_rewind_prev_key - @string/notification_action_close_key - @string/notification_action_n_a_key - - - notification_action_4_key - @string/notification_action_close_key - - @string/notification_action_close_value - @string/notification_action_repeat_value - @string/notification_action_shuffle_value - @string/notification_action_next_value - @string/notification_action_forward_value - @string/notification_action_smart_forward_next_value - @string/notification_action_n_a_value - - - @string/notification_action_close_key - @string/notification_action_repeat_key - @string/notification_action_shuffle_key - @string/notification_action_next_key - @string/notification_action_forward_key - @string/notification_action_smart_forward_next_key - @string/notification_action_n_a_key - + notification_slot_compact_0_key + notification_slot_compact_1_key + notification_slot_compact_2_key video_mp4 video_webm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45d806728..21c734741 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,21 +66,20 @@ Third action button Fourth action button Fifth action button - Notification compact view - Notification slots to show in compact view - - Previous - Next - Rewind - Forward - Rewind / Previous - Forward / Next - Play / Pause / Buffering - Play / Pause - Repeat - Shuffle - Close - Nothing + Edit each notification action below by tapping on it.\nSelect up to three of them to be shown in the compact notification by using the checkboxes on the right. + You can select at most three actions to show in the compact notification! + + Previous + Next + Rewind + Forward + Rewind / Previous + Forward / Next + Play / Pause / Buffering + Play / Pause + Repeat + Shuffle + Nothing Audio Default audio format @@ -154,7 +153,7 @@ Other Debug Updates - Notifications + Notification 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 7b25a4ab5..e45641f91 100644 --- a/app/src/main/res/xml/appearance_settings.xml +++ b/app/src/main/res/xml/appearance_settings.xml @@ -34,70 +34,4 @@ android:title="@string/caption_setting_title" app:iconSpaceReserved="false" /> - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml index 604f3bb05..af093a757 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/main/res/xml/main_settings.xml @@ -35,6 +35,12 @@ android:icon="?attr/ic_language" android:title="@string/content"/> + + Date: Tue, 8 Sep 2020 21:19:43 +0200 Subject: [PATCH 14/30] Make notification creation and cancelling more consistent --- .../org/schabi/newpipe/player/MainPlayer.java | 12 ++---- .../newpipe/player/NotificationUtil.java | 41 ++++++++++--------- .../newpipe/player/VideoPlayerImpl.java | 41 +++++++------------ 3 files changed, 39 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index a16fc3cbf..b885d105b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -121,7 +121,7 @@ public final class MainPlayer extends Service { if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) || intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) != null) { - showNotificationAndStartForeground(); + NotificationUtil.getInstance().createNotificationAndStartForeground(playerImpl, this); } playerImpl.handleIntent(intent); @@ -154,7 +154,7 @@ public final class MainPlayer extends Service { // So we should hide the notification at all. // When autoplay enabled such notification flashing is annoying so skip this case if (!autoplayEnabled) { - stopForeground(true); + NotificationUtil.getInstance().cancelNotificationAndStopForeground(this); } } } @@ -202,9 +202,8 @@ public final class MainPlayer extends Service { playerImpl.removePopupFromView(); playerImpl.destroy(); } - NotificationUtil.getInstance().cancelNotification(); - stopForeground(true); + NotificationUtil.getInstance().cancelNotificationAndStopForeground(this); stopSelf(); } @@ -243,11 +242,6 @@ public final class MainPlayer extends Service { } } - private void showNotificationAndStartForeground() { - NotificationUtil.getInstance().createNotificationIfNeeded(playerImpl, true); - NotificationUtil.getInstance().startForegroundServiceWithNotification(this); - } - public class LocalBinder extends Binder { diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 2c5e882bb..e05d7d19d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -42,7 +42,7 @@ import static org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE; * @author cool-student */ public final class NotificationUtil { - private static final String TAG = "NotificationUtil"; + private static final String TAG = NotificationUtil.class.getSimpleName(); private static final boolean DEBUG = BasePlayer.DEBUG; private static final int NOTIFICATION_ID = 123789; @@ -70,21 +70,25 @@ public final class NotificationUtil { ///////////////////////////////////////////////////// /** - * Creates the notification if it does not exist already or unless forceRecreate is true. + * Creates the notification if it does not exist already and recreates it if forceRecreate is + * true. Updates the notification with the data in the player. * @param player the player currently open, to take data from * @param forceRecreate whether to force the recreation of the notification even if it already * exists */ - void createNotificationIfNeeded(final VideoPlayerImpl player, final boolean forceRecreate) { + synchronized void createNotificationIfNeededAndUpdate(final VideoPlayerImpl player, + final boolean forceRecreate) { if (notificationBuilder == null || forceRecreate) { if (DEBUG) { - Log.d(TAG, "N_ createNotificationIfNeeded(true)"); + Log.d(TAG, "N_ createNotificationIfNeededAndUpdate(true)"); } notificationBuilder = createNotification(player); } + updateNotification(player); } - private NotificationCompat.Builder createNotification(final VideoPlayerImpl player) { + private synchronized NotificationCompat.Builder createNotification( + final VideoPlayerImpl player) { notificationManager = (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, @@ -115,17 +119,12 @@ public final class NotificationUtil { .setPriority(NotificationCompat.PRIORITY_HIGH) .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentTitle(player.getVideoTitle()) - .setContentText(player.getUploaderName()) .setColor(ContextCompat.getColor(player.context, R.color.gray)) .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, getIntentForNotification(player), FLAG_UPDATE_CURRENT)) .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); - updateActions(builder, player); - setLargeIcon(builder, player); - return builder; } @@ -133,7 +132,7 @@ public final class NotificationUtil { * Updates the notification and the button icons depending on the playback state. * @param player the player currently open, to take data from */ - synchronized void updateNotification(final VideoPlayerImpl player) { + private synchronized void updateNotification(final VideoPlayerImpl player) { if (DEBUG) { Log.d(TAG, "N_ updateNotification()"); } @@ -151,7 +150,15 @@ public final class NotificationUtil { } - void startForegroundServiceWithNotification(final Service service) { + boolean hasSlotWithBuffering() { + return notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING + || notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING; + } + + + void createNotificationAndStartForeground(final VideoPlayerImpl player, final Service service) { + createNotificationIfNeededAndUpdate(player, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { service.startForeground(NOTIFICATION_ID, notificationBuilder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); @@ -160,13 +167,7 @@ public final class NotificationUtil { } } - - boolean hasSlotWithBuffering() { - return notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING - || notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING; - } - - void cancelNotification() { + void cancelNotificationAndStopForeground(final Service service) { try { if (notificationManager != null) { notificationManager.cancel(NOTIFICATION_ID); @@ -176,6 +177,8 @@ public final class NotificationUtil { } notificationManager = null; notificationBuilder = null; + + service.stopForeground(true); } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index c1171efb7..e2e382bb2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -575,7 +575,7 @@ public class VideoPlayerImpl extends VideoPlayer void onShuffleOrRepeatModeChanged() { updatePlaybackButtons(); updatePlayback(); - resetNotification(false); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @Override @@ -612,7 +612,7 @@ public class VideoPlayerImpl extends VideoPlayer titleTextView.setText(tag.getMetadata().getName()); channelTextView.setText(tag.getMetadata().getUploaderName()); - resetNotification(false); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); updateMetadata(); } @@ -1059,9 +1059,7 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(false); - NotificationUtil.getInstance().createNotificationIfNeeded(this, false); - NotificationUtil.getInstance().updateNotification( - this); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @Override @@ -1077,7 +1075,7 @@ public class VideoPlayerImpl extends VideoPlayer isForwardPressed = false; isRewindPressed = false; } else { - NotificationUtil.getInstance().updateNotification(this); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } } } @@ -1097,7 +1095,7 @@ public class VideoPlayerImpl extends VideoPlayer checkLandscape(); getRootView().setKeepScreenOn(true); - resetNotification(false); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @Override @@ -1113,12 +1111,12 @@ public class VideoPlayerImpl extends VideoPlayer updateWindowFlags(IDLE_WINDOW_FLAGS); - resetNotification(false); - // Remove running notification when user don't want music (or video in popup) // to be played in background if (!minimizeOnPopupEnabled() && !backgroundPlaybackEnabled() && videoPlayerSelected()) { - service.stopForeground(true); + NotificationUtil.getInstance().cancelNotificationAndStopForeground(service); + } else { + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } getRootView().setKeepScreenOn(false); @@ -1130,9 +1128,7 @@ public class VideoPlayerImpl extends VideoPlayer animatePlayButtons(false, 100); getRootView().setKeepScreenOn(true); - NotificationUtil.getInstance().createNotificationIfNeeded(this, false); - NotificationUtil.getInstance().updateNotification( - this); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @@ -1146,9 +1142,7 @@ public class VideoPlayerImpl extends VideoPlayer getRootView().setKeepScreenOn(false); updateWindowFlags(IDLE_WINDOW_FLAGS); - NotificationUtil.getInstance().createNotificationIfNeeded(this, false); - NotificationUtil.getInstance().updateNotification(this); - + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); super.onCompleted(); } @@ -1239,7 +1233,7 @@ public class VideoPlayerImpl extends VideoPlayer onShuffleClicked(); break; case ACTION_RECREATE_NOTIFICATION: - resetNotification(true); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true); break; case Intent.ACTION_HEADSET_PLUG: //FIXME /*notificationManager.cancel(NOTIFICATION_ID); @@ -1309,19 +1303,12 @@ public class VideoPlayerImpl extends VideoPlayer // Thumbnail Loading //////////////////////////////////////////////////////////////////////////*/ - void resetNotification(final boolean recreate) { - NotificationUtil.getInstance().createNotificationIfNeeded(this, recreate); - NotificationUtil.getInstance().updateNotification(this); - } - @Override public void onLoadingComplete(final String imageUri, final View view, final Bitmap loadedImage) { - // rebuild OLD notification here since remote view does not release bitmaps, - // causing memory leaks super.onLoadingComplete(imageUri, view, loadedImage); - resetNotification(true); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @Override @@ -1329,13 +1316,13 @@ public class VideoPlayerImpl extends VideoPlayer final View view, final FailReason failReason) { super.onLoadingFailed(imageUri, view, failReason); - resetNotification(true); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } @Override public void onLoadingCancelled(final String imageUri, final View view) { super.onLoadingCancelled(imageUri, view); - resetNotification(true); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } /*////////////////////////////////////////////////////////////////////////// From bc8954fbbafc737cd950a2b86fe89694d87f0120 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 8 Sep 2020 21:42:25 +0200 Subject: [PATCH 15/30] Fix notification content intent not being updated when needed --- .../newpipe/player/NotificationUtil.java | 14 +-- .../schabi/newpipe/util/NavigationHelper.java | 86 +++++++++---------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index e05d7d19d..e01b0ec49 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -120,8 +120,6 @@ public final class NotificationUtil { .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setColor(ContextCompat.getColor(player.context, R.color.gray)) - .setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, - getIntentForNotification(player), FLAG_UPDATE_CURRENT)) .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); @@ -141,6 +139,9 @@ public final class NotificationUtil { return; } + // also update content intent, in case the user switched players + notificationBuilder.setContentIntent(PendingIntent.getActivity(player.context, + NOTIFICATION_ID, getIntentForNotification(player), FLAG_UPDATE_CURRENT)); notificationBuilder.setContentTitle(player.getVideoTitle()); notificationBuilder.setContentText(player.getUploaderName()); updateActions(notificationBuilder, player); @@ -326,19 +327,18 @@ public final class NotificationUtil { } private Intent getIntentForNotification(final VideoPlayerImpl player) { - final Intent intent; if (player.audioPlayerSelected() || player.popupPlayerSelected()) { - // Means we play in popup or audio only. Let's show BackgroundPlayerActivity - intent = NavigationHelper.getBackgroundPlayerActivityIntent(player.context); + // Means we play in popup or audio only. Let's show the play queue + return NavigationHelper.getPlayQueueActivityIntent(player.context); } else { // We are playing in fragment. Don't open another activity just show fragment. That's it - intent = NavigationHelper.getPlayerIntent( + final Intent intent = NavigationHelper.getPlayerIntent( player.context, MainActivity.class, null, true); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); + return intent; } - return intent; } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 96dfc1925..fb1469802 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -57,7 +57,7 @@ import org.schabi.newpipe.settings.SettingsActivity; import java.util.ArrayList; -@SuppressWarnings({"unused", "WeakerAccess"}) +@SuppressWarnings({"unused"}) public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; @@ -69,16 +69,18 @@ public final class NavigationHelper { //////////////////////////////////////////////////////////////////////////*/ @NonNull - public static Intent getPlayerIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @NonNull final PlayQueue playQueue, - @Nullable final String quality, - final boolean resumePlayback) { + public static Intent getPlayerIntent(@NonNull final Context context, + @NonNull final Class targetClazz, + @Nullable final PlayQueue playQueue, + @Nullable final String quality, + final boolean resumePlayback) { final Intent intent = new Intent(context, targetClazz); - final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class); - if (cacheKey != null) { - intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); + if (playQueue != null) { + final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class); + if (cacheKey != null) { + intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); + } } if (quality != null) { intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality); @@ -90,53 +92,51 @@ public final class NavigationHelper { } @NonNull - public static Intent getPlayerIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @NonNull final PlayQueue playQueue, - final boolean resumePlayback) { + public static Intent getPlayerIntent(@NonNull final Context context, + @NonNull final Class targetClazz, + @Nullable final PlayQueue playQueue, + final boolean resumePlayback) { return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback); } @NonNull - public static Intent getPlayerEnqueueIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @NonNull final PlayQueue playQueue, - final boolean selectOnAppend, - final boolean resumePlayback) { + public static Intent getPlayerEnqueueIntent(@NonNull final Context context, + @NonNull final Class targetClazz, + @Nullable final PlayQueue playQueue, + final boolean selectOnAppend, + final boolean resumePlayback) { return getPlayerIntent(context, targetClazz, playQueue, resumePlayback) .putExtra(BasePlayer.APPEND_ONLY, true) .putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend); } @NonNull - public static Intent getPlayerIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @NonNull final PlayQueue playQueue, - final int repeatMode, - final float playbackSpeed, - final float playbackPitch, - final boolean playbackSkipSilence, - @Nullable final String playbackQuality, - final boolean resumePlayback, - final boolean startPaused, - final boolean isMuted) { + public static Intent getPlayerIntent(@NonNull final Context context, + @NonNull final Class targetClazz, + @Nullable final PlayQueue playQueue, + final int repeatMode, + final float playbackSpeed, + final float playbackPitch, + final boolean playbackSkipSilence, + @Nullable final String playbackQuality, + final boolean resumePlayback, + final boolean startPaused, + final boolean isMuted) { return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback) .putExtra(BasePlayer.REPEAT_MODE, repeatMode) .putExtra(BasePlayer.START_PAUSED, startPaused) .putExtra(BasePlayer.IS_MUTED, isMuted); } - public static void playOnMainPlayer( - final AppCompatActivity activity, - final PlayQueue queue, - final boolean autoPlay) { + public static void playOnMainPlayer(final AppCompatActivity activity, + final PlayQueue queue, + final boolean autoPlay) { playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay); } - public static void playOnMainPlayer( - final FragmentManager fragmentManager, - final PlayQueue queue, - final boolean autoPlay) { + public static void playOnMainPlayer(final FragmentManager fragmentManager, + final PlayQueue queue, + final boolean autoPlay) { final PlayQueueItem currentStream = queue.getItem(); openVideoDetailFragment( fragmentManager, @@ -148,7 +148,7 @@ public final class NavigationHelper { } public static void playOnMainPlayer(@NonNull final Context context, - @NonNull final PlayQueue queue, + @Nullable final PlayQueue queue, @NonNull final StreamingService.LinkType linkType, @NonNull final String url, @NonNull final String title, @@ -553,18 +553,14 @@ public final class NavigationHelper { return true; } - public static Intent getBackgroundPlayerActivityIntent(final Context context) { - return getServicePlayerActivityIntent(context, BackgroundPlayerActivity.class); - } - - private static Intent getServicePlayerActivityIntent(final Context context, - final Class activityClass) { - final Intent intent = new Intent(context, activityClass); + public static Intent getPlayQueueActivityIntent(final Context context) { + final Intent intent = new Intent(context, BackgroundPlayerActivity.class); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } return intent; } + /*////////////////////////////////////////////////////////////////////////// // Link handling //////////////////////////////////////////////////////////////////////////*/ From bd34c7ede3a7e01b19e6f5a1a8f9944db231826f Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 8 Sep 2020 21:59:39 +0200 Subject: [PATCH 16/30] Make player foreground playback-specific in manifest --- app/src/main/AndroidManifest.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b3b80d88..6fd62aebe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,8 +44,9 @@ + android:name=".player.MainPlayer" + android:exported="false" + android:foregroundServiceType="mediaPlayback"> From 1605e50cef85e54a7ff6faee6519ba13c45c0b74 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 9 Sep 2020 20:30:57 +0200 Subject: [PATCH 17/30] Update notification when play queue is edited --- .../schabi/newpipe/player/VideoPlayerImpl.java | 6 ++++++ .../player/playback/MediaSourceManager.java | 16 ++++++++-------- .../player/playback/PlaybackListener.java | 11 ++++++++++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index dd23b47e6..7e02549f1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -642,6 +642,12 @@ public class VideoPlayerImpl extends VideoPlayer duration); } + @Override + public void onPlayQueueEdited() { + updatePlayback(); + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); + } + @Override @Nullable public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) { diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index 23e813c4b..1b8c62e64 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -255,21 +255,21 @@ public class MediaSourceManager { // Loading and Syncing switch (event.type()) { - case INIT: - case REORDER: - case ERROR: - case SELECT: + case INIT: case REORDER: case ERROR: case SELECT: loadImmediate(); // low frequency, critical events break; - case APPEND: - case REMOVE: - case MOVE: - case RECOVERY: + case APPEND: case REMOVE: case MOVE: case RECOVERY: default: loadDebounced(); // high frequency or noncritical events break; } + // update ui and notification + switch (event.type()) { + case APPEND: case REMOVE: case MOVE: case REORDER: + playbackListener.onPlayQueueEdited(); + } + if (!isPlayQueueReady()) { maybeBlock(); playQueue.fetch(); diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java index 0755bdd7a..811f82b3b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java @@ -69,7 +69,7 @@ public interface PlaybackListener { MediaSource sourceOf(PlayQueueItem item, StreamInfo info); /** - * Called when the play queue can no longer to played or used. + * Called when the play queue can no longer be played or used. * Currently, this means the play queue is empty and complete. * Signals to the listener that it should shutdown. *

@@ -77,4 +77,13 @@ public interface PlaybackListener { *

*/ void onPlaybackShutdown(); + + /** + * Called whenever the play queue was edited (items were added, deleted or moved), + * use this to e.g. update notification buttons or fragment ui. + *

+ * May be called at any time. + *

+ */ + void onPlayQueueEdited(); } From 52e89c1d1cea4226ec25c0708ecd0dea75794108 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 9 Sep 2020 20:41:30 +0200 Subject: [PATCH 18/30] Prevent seeking out of video duration in player --- .../java/org/schabi/newpipe/player/BasePlayer.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index a26ec6ccb..5a60095ae 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -1242,7 +1242,15 @@ public abstract class BasePlayer implements Log.d(TAG, "seekBy() called with: position = [" + positionMillis + "]"); } if (simpleExoPlayer != null) { - simpleExoPlayer.seekTo(positionMillis); + // prevent invalid positions when fast-forwarding/-rewinding + long normalizedPositionMillis = positionMillis; + if (normalizedPositionMillis < 0) { + normalizedPositionMillis = 0; + } else if (normalizedPositionMillis > simpleExoPlayer.getDuration()) { + normalizedPositionMillis = simpleExoPlayer.getDuration(); + } + + simpleExoPlayer.seekTo(normalizedPositionMillis); } } From 5846fbabce47554d5596433c80c77f55d1e86ed0 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 9 Sep 2020 20:44:27 +0200 Subject: [PATCH 19/30] Change "image" to "thumbnail" --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21c734741..1f4be4c83 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,8 +59,8 @@ Show \"Play with Kodi\" option Display an option to play a video via Kodi media center - Scale image to 1:1 aspect ratio - This will scale the notification image from 16:9 to 1:1 aspect ratio + Scale thumbnail to 1:1 aspect ratio + Scale the video thumbnail shown in the notification from 16:9 to 1:1 aspect ratio (may introduce distortions) First action button Second action button Third action button From bccfe500b39de8416f1deac9cc4a9468d8bf01cd Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 10 Sep 2020 18:47:22 +0200 Subject: [PATCH 20/30] Fix seekbar invisible or not updating Have the notification recreate only when strictly necessary, and recreate it if there was a timeline change, fixing the seekbar not updating at all sometimes --- .../org/schabi/newpipe/player/NotificationUtil.java | 12 ++++++------ .../org/schabi/newpipe/player/VideoPlayerImpl.java | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index e01b0ec49..b0bfcb6e1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -78,10 +78,7 @@ public final class NotificationUtil { */ synchronized void createNotificationIfNeededAndUpdate(final VideoPlayerImpl player, final boolean forceRecreate) { - if (notificationBuilder == null || forceRecreate) { - if (DEBUG) { - Log.d(TAG, "N_ createNotificationIfNeededAndUpdate(true)"); - } + if (forceRecreate || notificationBuilder == null) { notificationBuilder = createNotification(player); } updateNotification(player); @@ -89,6 +86,9 @@ public final class NotificationUtil { private synchronized NotificationCompat.Builder createNotification( final VideoPlayerImpl player) { + if (DEBUG) { + Log.d(TAG, "createNotification()"); + } notificationManager = (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, @@ -132,7 +132,7 @@ public final class NotificationUtil { */ private synchronized void updateNotification(final VideoPlayerImpl player) { if (DEBUG) { - Log.d(TAG, "N_ updateNotification()"); + Log.d(TAG, "updateNotification()"); } if (notificationBuilder == null) { @@ -158,7 +158,7 @@ public final class NotificationUtil { void createNotificationAndStartForeground(final VideoPlayerImpl player, final Service service) { - createNotificationIfNeededAndUpdate(player, true); + createNotificationIfNeededAndUpdate(player, false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { service.startForeground(NOTIFICATION_ID, notificationBuilder.build(), diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 7e02549f1..f30330e4d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -66,6 +66,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.text.CaptionStyleCompat; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -604,6 +605,13 @@ public class VideoPlayerImpl extends VideoPlayer } } + @Override + public void onTimelineChanged(final Timeline timeline, final int reason) { + super.onTimelineChanged(timeline, reason); + // force recreate notification to ensure seek bar is shown when preparation finishes + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, true); + } + protected void onMetadataChanged(@NonNull final MediaSourceTag tag) { super.onMetadataChanged(tag); From 2017e6a3e31d2a03a399c82f712001a8e3c01a05 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 10 Sep 2020 20:36:52 +0200 Subject: [PATCH 21/30] Refactor MediaSessionManager --- .../player/helper/MediaSessionManager.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) 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 8d089c6ed..b0c641433 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 @@ -16,14 +16,14 @@ import androidx.media.session.MediaButtonReceiver; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; -import org.schabi.newpipe.BuildConfig; +import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.player.mediasession.MediaSessionCallback; import org.schabi.newpipe.player.mediasession.PlayQueueNavigator; import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController; public class MediaSessionManager { - private static final String TAG = "MediaSessionManager"; - public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); + private static final String TAG = MediaSessionManager.class.getSimpleName(); + public static final boolean DEBUG = MainActivity.DEBUG; @NonNull private final MediaSessionCompat mediaSession; @@ -35,12 +35,12 @@ public class MediaSessionManager { 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 + mediaSession = new MediaSessionCompat(context, TAG); + mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); - this.mediaSession.setActive(true); + mediaSession.setActive(true); - this.mediaSession.setPlaybackState(new PlaybackStateCompat.Builder() + mediaSession.setPlaybackState(new PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_NONE, -1, 1) .setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PLAY @@ -51,10 +51,10 @@ public class MediaSessionManager { | PlaybackStateCompat.ACTION_STOP) .build()); - this.sessionConnector = new MediaSessionConnector(mediaSession); - this.sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback)); - this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback)); - this.sessionConnector.setPlayer(player); + sessionConnector = new MediaSessionConnector(mediaSession); + sessionConnector.setControlDispatcher(new PlayQueuePlaybackController(callback)); + sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback)); + sessionConnector.setPlayer(player); } @Nullable @@ -64,10 +64,12 @@ public class MediaSessionManager { } public MediaSessionCompat.Token getSessionToken() { - return this.mediaSession.getSessionToken(); + return mediaSession.getSessionToken(); } - public void setMetadata(final String title, final String artist, final Bitmap albumArt, + public void setMetadata(final String title, + final String artist, + final Bitmap albumArt, final long duration) { if (albumArt == null || !mediaSession.isActive()) { return; @@ -130,9 +132,9 @@ public class MediaSessionManager { * Should be called on player destruction to prevent leakage. */ public void dispose() { - this.sessionConnector.setPlayer(null); - this.sessionConnector.setQueueNavigator(null); - this.mediaSession.setActive(false); - this.mediaSession.release(); + sessionConnector.setPlayer(null); + sessionConnector.setQueueNavigator(null); + mediaSession.setActive(false); + mediaSession.release(); } } From 59e7ebabfa1514607e2ae0584539b8a021cbdce9 Mon Sep 17 00:00:00 2001 From: Stypox Date: Wed, 16 Sep 2020 14:00:22 +0200 Subject: [PATCH 22/30] Random adjustements to notification --- .../newpipe/player/NotificationUtil.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index b0bfcb6e1..cde280793 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.player; import android.annotation.SuppressLint; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; @@ -14,6 +13,7 @@ import android.util.Log; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import org.schabi.newpipe.MainActivity; @@ -23,7 +23,6 @@ import org.schabi.newpipe.util.NavigationHelper; import java.util.List; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; -import static android.content.Context.NOTIFICATION_SERVICE; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; import static org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING; @@ -51,7 +50,7 @@ public final class NotificationUtil { @NotificationConstants.Action private int[] notificationSlots = NotificationConstants.SLOT_DEFAULTS.clone(); - private NotificationManager notificationManager; + private NotificationManagerCompat notificationManager; private NotificationCompat.Builder notificationBuilder; private NotificationUtil() { @@ -82,6 +81,7 @@ public final class NotificationUtil { notificationBuilder = createNotification(player); } updateNotification(player); + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); } private synchronized NotificationCompat.Builder createNotification( @@ -89,8 +89,7 @@ public final class NotificationUtil { if (DEBUG) { Log.d(TAG, "createNotification()"); } - notificationManager = - (NotificationManager) player.context.getSystemService(NOTIFICATION_SERVICE); + notificationManager = NotificationManagerCompat.from(player.context); final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.context, player.context.getString(R.string.notification_channel_id)); @@ -120,6 +119,7 @@ public final class NotificationUtil { .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setColor(ContextCompat.getColor(player.context, R.color.gray)) + .setCategory(NotificationCompat.CATEGORY_TRANSPORT) .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); @@ -127,7 +127,7 @@ public final class NotificationUtil { } /** - * Updates the notification and the button icons depending on the playback state. + * Updates the notification builder and the button icons depending on the playback state. * @param player the player currently open, to take data from */ private synchronized void updateNotification(final VideoPlayerImpl player) { @@ -135,19 +135,14 @@ public final class NotificationUtil { Log.d(TAG, "updateNotification()"); } - if (notificationBuilder == null) { - return; - } - // also update content intent, in case the user switched players notificationBuilder.setContentIntent(PendingIntent.getActivity(player.context, NOTIFICATION_ID, getIntentForNotification(player), FLAG_UPDATE_CURRENT)); notificationBuilder.setContentTitle(player.getVideoTitle()); notificationBuilder.setContentText(player.getUploaderName()); + notificationBuilder.setTicker(player.getVideoTitle()); updateActions(notificationBuilder, player); setLargeIcon(notificationBuilder, player); - - notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); } @@ -158,7 +153,10 @@ public final class NotificationUtil { void createNotificationAndStartForeground(final VideoPlayerImpl player, final Service service) { - createNotificationIfNeededAndUpdate(player, false); + if (notificationBuilder == null) { + notificationBuilder = createNotification(player); + } + updateNotification(player); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { service.startForeground(NOTIFICATION_ID, notificationBuilder.build(), @@ -169,17 +167,13 @@ public final class NotificationUtil { } void cancelNotificationAndStopForeground(final Service service) { - try { - if (notificationManager != null) { - notificationManager.cancel(NOTIFICATION_ID); - } - } catch (final Exception e) { - Log.e(TAG, "Could not cancel notification", e); + service.stopForeground(true); + + if (notificationManager != null) { + notificationManager.cancel(NOTIFICATION_ID); } notificationManager = null; notificationBuilder = null; - - service.stopForeground(true); } From 7317737e90ef096fd72b3d1df1e16d04ff2d1d06 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 19 Sep 2020 13:44:07 +0200 Subject: [PATCH 23/30] Fix invisible queue close button --- app/src/main/res/layout-large-land/player.xml | 15 +++++++-------- app/src/main/res/layout/player.xml | 17 ++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/layout-large-land/player.xml b/app/src/main/res/layout-large-land/player.xml index 3fa187906..af8ca085e 100644 --- a/app/src/main/res/layout-large-land/player.xml +++ b/app/src/main/res/layout-large-land/player.xml @@ -469,17 +469,16 @@ android:id="@+id/playQueueClose" android:layout_width="50dp" android:layout_height="50dp" - android:layout_centerVertical="true" android:layout_alignParentEnd="true" + android:layout_centerVertical="true" android:layout_marginEnd="40dp" - android:padding="10dp" - android:clickable="true" - android:focusable="true" - android:scaleType="fitXY" - android:tint="?attr/colorAccent" - app:srcCompat="@drawable/ic_close_white_24dp" android:background="?android:selectableItemBackground" - tools:ignore="ContentDescription"/> + android:clickable="true" + android:contentDescription="@string/close" + android:focusable="true" + android:padding="10dp" + android:scaleType="fitXY" + app:srcCompat="@drawable/ic_close_white_24dp" /> - + android:clickable="true" + android:contentDescription="@string/close" + android:focusable="true" + android:padding="10dp" + android:scaleType="fitXY" + app:srcCompat="@drawable/ic_close_white_24dp" /> Date: Tue, 22 Sep 2020 16:46:09 +0200 Subject: [PATCH 24/30] Remove hardcoded and duplicate strings, use exoplayer ones --- .../newpipe/player/NotificationConstants.java | 58 +++++++--- .../newpipe/player/NotificationUtil.java | 103 +++++++++--------- .../NotificationSettingsFragment.java | 7 +- .../res/layout/related_streams_header.xml | 2 +- app/src/main/res/values/strings.xml | 11 +- 5 files changed, 95 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java b/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java index 599e18e65..cf58c8f76 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationConstants.java @@ -6,9 +6,9 @@ import android.content.SharedPreferences; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; -import androidx.annotation.StringRes; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.Localization; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -41,22 +41,6 @@ public final class NotificationConstants { PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE}) public @interface Action { } - @StringRes - public static final int[] ACTION_SUMMARIES = { - R.string.notification_action_nothing, - R.string.notification_action_previous, - R.string.notification_action_next, - R.string.notification_action_rewind, - R.string.notification_action_forward, - R.string.notification_action_smart_rewind_previous, - R.string.notification_action_smart_forward_next, - R.string.notification_action_play_pause, - R.string.notification_action_play_pause_buffering, - R.string.notification_action_repeat, - R.string.notification_action_shuffle, - R.string.close, - }; - @DrawableRes public static final int[] ACTION_ICONS = { 0, @@ -110,6 +94,46 @@ public final class NotificationConstants { R.string.notification_slot_compact_2_key, }; + + public static String getActionName(@NonNull final Context context, @Action final int action) { + switch (action) { + case PREVIOUS: + return context.getString(R.string.exo_controls_previous_description); + case NEXT: + return context.getString(R.string.exo_controls_next_description); + case REWIND: + return context.getString(R.string.exo_controls_rewind_description); + case FORWARD: + return context.getString(R.string.exo_controls_fastforward_description); + case SMART_REWIND_PREVIOUS: + return Localization.concatenateStrings( + context.getString(R.string.exo_controls_rewind_description), + context.getString(R.string.exo_controls_previous_description)); + case SMART_FORWARD_NEXT: + return Localization.concatenateStrings( + context.getString(R.string.exo_controls_fastforward_description), + context.getString(R.string.exo_controls_next_description)); + case PLAY_PAUSE: + return Localization.concatenateStrings( + context.getString(R.string.exo_controls_play_description), + context.getString(R.string.exo_controls_pause_description)); + case PLAY_PAUSE_BUFFERING: + return Localization.concatenateStrings( + context.getString(R.string.exo_controls_play_description), + context.getString(R.string.exo_controls_pause_description), + context.getString(R.string.notification_action_buffering)); + case REPEAT: + return context.getString(R.string.notification_action_repeat); + case SHUFFLE: + return context.getString(R.string.notification_action_shuffle); + case CLOSE: + return context.getString(R.string.close); + case NOTHING: default: + return context.getString(R.string.notification_action_nothing); + } + } + + /** * @param context the context to use * @param sharedPreferences the shared preferences to query values from diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index cde280793..577c82557 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -12,6 +12,7 @@ import android.util.Log; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; @@ -214,75 +215,89 @@ public final class NotificationUtil { final int baseActionIcon = NotificationConstants.ACTION_ICONS[selectedAction]; switch (selectedAction) { case NotificationConstants.PREVIOUS: - return getAction(player, baseActionIcon, "Previous", ACTION_PLAY_PREVIOUS); + return getAction(player, baseActionIcon, + R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS); case NotificationConstants.NEXT: - return getAction(player, baseActionIcon, "Next", ACTION_PLAY_NEXT); + return getAction(player, baseActionIcon, + R.string.exo_controls_next_description, ACTION_PLAY_NEXT); case NotificationConstants.REWIND: - return getAction(player, baseActionIcon, "Rewind", ACTION_FAST_REWIND); + return getAction(player, baseActionIcon, + R.string.exo_controls_rewind_description, ACTION_FAST_REWIND); case NotificationConstants.FORWARD: - return getAction(player, baseActionIcon, "Forward", ACTION_FAST_FORWARD); + return getAction(player, baseActionIcon, + R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); case NotificationConstants.SMART_REWIND_PREVIOUS: if (player.playQueue != null && player.playQueue.size() > 1) { return getAction(player, R.drawable.exo_notification_previous, - "Previous", ACTION_PLAY_PREVIOUS); + R.string.exo_controls_previous_description, ACTION_PLAY_PREVIOUS); } else { return getAction(player, R.drawable.exo_controls_rewind, - "Rewind", ACTION_FAST_REWIND); + R.string.exo_controls_rewind_description, ACTION_FAST_REWIND); } case NotificationConstants.SMART_FORWARD_NEXT: if (player.playQueue != null && player.playQueue.size() > 1) { return getAction(player, R.drawable.exo_notification_next, - "Next", ACTION_PLAY_NEXT); + R.string.exo_controls_next_description, ACTION_PLAY_NEXT); } else { return getAction(player, R.drawable.exo_controls_fastforward, - "Forward", ACTION_FAST_FORWARD); + R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); } case NotificationConstants.PLAY_PAUSE: - final boolean pauseOrPlay = player.isPlaying() + if (player.isPlaying() || player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED - || player.getCurrentState() == BasePlayer.STATE_BUFFERING; - return getAction(player, - pauseOrPlay ? R.drawable.exo_notification_pause - : R.drawable.exo_notification_play, - pauseOrPlay ? "Pause" : "Play", - ACTION_PLAY_PAUSE); + || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { + return getAction(player, R.drawable.exo_notification_pause, + R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); + } else { + return getAction(player, R.drawable.exo_notification_play, + R.string.exo_controls_play_description, ACTION_PLAY_PAUSE); + } case NotificationConstants.PLAY_PAUSE_BUFFERING: if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { return getAction(player, R.drawable.ic_hourglass_top_white_24dp_png, - "Buffering", ACTION_BUFFERING); + R.string.notification_action_buffering, ACTION_BUFFERING); + } else if (player.isPlaying()) { + return getAction(player, R.drawable.exo_notification_pause, + R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); } else { - return getAction(player, - player.isPlaying() ? R.drawable.exo_notification_pause - : R.drawable.exo_notification_play, - player.isPlaying() ? "Pause" : "Play", - ACTION_PLAY_PAUSE); + return getAction(player, R.drawable.exo_notification_play, + R.string.exo_controls_play_description, ACTION_PLAY_PAUSE); } case NotificationConstants.REPEAT: - return getAction(player, getRepeatModeDrawable(player.getRepeatMode()), - getRepeatModeTitle(player.getRepeatMode()), ACTION_REPEAT); + if (player.getRepeatMode() == REPEAT_MODE_ALL) { + return getAction(player, R.drawable.exo_media_action_repeat_all, + R.string.exo_controls_repeat_all_description, ACTION_REPEAT); + } else if (player.getRepeatMode() == REPEAT_MODE_ONE) { + return getAction(player, R.drawable.exo_media_action_repeat_one, + R.string.exo_controls_repeat_one_description, ACTION_REPEAT); + } else /* player.getRepeatMode() == REPEAT_MODE_OFF */ { + return getAction(player, R.drawable.exo_media_action_repeat_off, + R.string.exo_controls_repeat_off_description, ACTION_REPEAT); + } case NotificationConstants.SHUFFLE: - final boolean shuffled = player.playQueue != null && player.playQueue.isShuffled(); - return getAction(player, - shuffled ? R.drawable.exo_controls_shuffle_on - : R.drawable.exo_controls_shuffle_off, - shuffled ? "ShuffleOn" : "ShuffleOff", - ACTION_SHUFFLE); + if (player.playQueue != null && player.playQueue.isShuffled()) { + return getAction(player, R.drawable.exo_controls_shuffle_on, + R.string.exo_controls_shuffle_on_description, ACTION_SHUFFLE); + } else { + return getAction(player, R.drawable.exo_controls_shuffle_off, + R.string.exo_controls_shuffle_off_description, ACTION_SHUFFLE); + } case NotificationConstants.CLOSE: return getAction(player, R.drawable.ic_close_white_24dp_png, - "Close", ACTION_CLOSE); + R.string.close, ACTION_CLOSE); case NotificationConstants.NOTHING: default: @@ -293,31 +308,11 @@ public final class NotificationUtil { private NotificationCompat.Action getAction(final VideoPlayerImpl player, @DrawableRes final int drawable, - final String title, + @StringRes final int title, final String intentAction) { - return new NotificationCompat.Action(drawable, title, PendingIntent.getBroadcast( - player.context, NOTIFICATION_ID, new Intent(intentAction), FLAG_UPDATE_CURRENT)); - } - - @DrawableRes - private int getRepeatModeDrawable(final int repeatMode) { - if (repeatMode == REPEAT_MODE_ALL) { - return R.drawable.exo_controls_repeat_all; - } else if (repeatMode == REPEAT_MODE_ONE) { - return R.drawable.exo_controls_repeat_one; - } else /* repeatMode == REPEAT_MODE_OFF */ { - return R.drawable.exo_controls_repeat_off; - } - } - - private String getRepeatModeTitle(final int repeatMode) { - if (repeatMode == REPEAT_MODE_ALL) { - return "RepeatAll"; - } else if (repeatMode == REPEAT_MODE_ONE) { - return "RepeatOne"; - } else /* repeatMode == REPEAT_MODE_OFF */ { - return "RepeatOff"; - } + return new NotificationCompat.Action(drawable, player.context.getString(title), + PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, + new Intent(intentAction), FLAG_UPDATE_CURRENT)); } private Intent getIntentForNotification(final VideoPlayerImpl player) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java index f4bbc96a7..ce1e9e5a5 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java @@ -209,7 +209,7 @@ public class NotificationSettingsFragment extends Fragment { NotificationConstants.ACTION_ICONS[selectedAction])); } - summary.setText(NotificationConstants.ACTION_SUMMARIES[selectedAction]); + summary.setText(NotificationConstants.getActionName(requireContext(), selectedAction)); } void openActionChooserDialog() { @@ -225,8 +225,7 @@ public class NotificationSettingsFragment extends Fragment { .create(); final View.OnClickListener radioButtonsClickListener = v -> { - final int id = ((RadioButton) v).getId(); - selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][id]; + selectedAction = NotificationConstants.SLOT_ALLOWED_ACTIONS[i][v.getId()]; updateInfo(); alertDialog.dismiss(); }; @@ -253,7 +252,7 @@ public class NotificationSettingsFragment extends Fragment { } } - radioButton.setText(NotificationConstants.ACTION_SUMMARIES[action]); + radioButton.setText(NotificationConstants.getActionName(requireContext(), action)); radioButton.setChecked(action == selectedAction); radioButton.setId(id); radioButton.setLayoutParams(new RadioGroup.LayoutParams( diff --git a/app/src/main/res/layout/related_streams_header.xml b/app/src/main/res/layout/related_streams_header.xml index b98244b7e..77be2247b 100644 --- a/app/src/main/res/layout/related_streams_header.xml +++ b/app/src/main/res/layout/related_streams_header.xml @@ -12,7 +12,7 @@ android:layout_height="wrap_content" android:layout_marginLeft="12dp" android:layout_alignBaseline="@+id/autoplay_switch" - android:text="@string/next_video_title" + android:text="@string/exo_controls_next_description" android:textAppearance="?android:attr/textAppearanceMedium" android:textSize="12sp" tools:ignore="RtlHardcoded" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1f4be4c83..c9119783b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,16 +69,9 @@ Edit each notification action below by tapping on it.\nSelect up to three of them to be shown in the compact notification by using the checkboxes on the right. You can select at most three actions to show in the compact notification! - Previous - Next - Rewind - Forward - Rewind / Previous - Forward / Next - Play / Pause / Buffering - Play / Pause Repeat Shuffle + Buffering Nothing Audio @@ -126,7 +119,6 @@ Resume playing Continue playing after interruptions (e.g. phonecalls) Download - Next Autoplay Show \'Next\' and \'Similar\' videos Show \"Hold to append\" tip @@ -159,7 +151,6 @@ Queued on background player Queued on popup player https://www.c3s.cc/ - Play Content Age restricted content Show age restricted video. Future changes are possible from the settings. From 814efbf8df0eb21594cdb30232ee545b12a84ce0 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 22 Sep 2020 17:36:39 +0200 Subject: [PATCH 25/30] Remove ACTION_BUFFERING, update buffering only if needed - ACTION_BUFFERING was just wrong: why should the user be able to trigger the internal onBuffering() state by pressing on the buffering button? So that was replaced by a null intent, doing nothing. - Now updating notification in onBuffering() only when buffering actions are not already buffering, to prevent useless updates --- .../org/schabi/newpipe/player/MainPlayer.java | 2 -- .../newpipe/player/NotificationUtil.java | 23 ++++++++++++++----- .../newpipe/player/VideoPlayerImpl.java | 21 ++--------------- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index 3c38f7460..6c33f1d47 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -76,8 +76,6 @@ public final class MainPlayer extends Service { = "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND"; static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD"; - static final String ACTION_BUFFERING - = "org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING"; static final String ACTION_SHUFFLE = "org.schabi.newpipe.player.MainPlayer.ACTION_SHUFFLE"; public static final String ACTION_RECREATE_NOTIFICATION diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 577c82557..1656d60b1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -26,7 +26,6 @@ import java.util.List; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING; import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND; @@ -147,9 +146,19 @@ public final class NotificationUtil { } - boolean hasSlotWithBuffering() { - return notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING - || notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING; + @SuppressLint("RestrictedApi") + boolean shouldUpdateBufferingSlot() { + if (notificationBuilder.mActions.size() < 3) { + // this should never happen, but let's make sure notification actions are populated + return true; + } + + // only second and third slot could contain PLAY_PAUSE_BUFFERING, update them only if they + // are not already in the buffering state (the only one with a null action intent) + return (notificationSlots[1] == NotificationConstants.PLAY_PAUSE_BUFFERING + && notificationBuilder.mActions.get(1).actionIntent != null) + || (notificationSlots[2] == NotificationConstants.PLAY_PAUSE_BUFFERING + && notificationBuilder.mActions.get(2).actionIntent != null); } @@ -264,8 +273,10 @@ public final class NotificationUtil { if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { - return getAction(player, R.drawable.ic_hourglass_top_white_24dp_png, - R.string.notification_action_buffering, ACTION_BUFFERING); + // null intent -> show hourglass icon that does nothing when clicked + return new NotificationCompat.Action(R.drawable.ic_hourglass_top_white_24dp_png, + player.context.getString(R.string.notification_action_buffering), + null); } else if (player.isPlaying()) { return getAction(player, R.drawable.exo_notification_pause, R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index f30330e4d..9bca8c968 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -105,7 +105,6 @@ import java.util.List; import static android.content.Context.WINDOW_SERVICE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static org.schabi.newpipe.player.MainPlayer.ACTION_BUFFERING; import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND; @@ -191,8 +190,6 @@ public class VideoPlayerImpl extends VideoPlayer private boolean isVerticalVideo = false; private boolean fragmentIsVisible = false; boolean shouldUpdateOnProgress; - private boolean isForwardPressed; - private boolean isRewindPressed; private final MainPlayer service; private PlayerServiceEventListener fragmentListener; @@ -1081,16 +1078,8 @@ public class VideoPlayerImpl extends VideoPlayer super.onBuffering(); getRootView().setKeepScreenOn(true); - if (NotificationUtil.getInstance().hasSlotWithBuffering() - && (getCurrentState() == BasePlayer.STATE_PREFLIGHT - || getCurrentState() == BasePlayer.STATE_BLOCKED - || getCurrentState() == BasePlayer.STATE_BUFFERING)) { - if (isForwardPressed || isRewindPressed) { - isForwardPressed = false; - isRewindPressed = false; - } else { - NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); - } + if (NotificationUtil.getInstance().shouldUpdateBufferingSlot()) { + NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); } } @@ -1186,7 +1175,6 @@ public class VideoPlayerImpl extends VideoPlayer 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(ACTION_RECREATE_NOTIFICATION); @@ -1222,11 +1210,9 @@ public class VideoPlayerImpl extends VideoPlayer onPlayPrevious(); break; case ACTION_FAST_FORWARD: - isForwardPressed = true; onFastForward(); break; case ACTION_FAST_REWIND: - isRewindPressed = true; onFastRewind(); break; case ACTION_PLAY_PAUSE: @@ -1240,9 +1226,6 @@ public class VideoPlayerImpl extends VideoPlayer case ACTION_REPEAT: onRepeatClicked(); break; - case ACTION_BUFFERING: - onBuffering(); - break; case ACTION_SHUFFLE: onShuffleClicked(); break; From b4e073cde7d737ea6c6230ecb17f47acac1da0b8 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 22 Sep 2020 18:12:51 +0200 Subject: [PATCH 26/30] Show replay icon in notification when player state is completed --- .../newpipe/player/NotificationUtil.java | 23 ++++++++---------- .../ic_replay_white_24dp_png.png | Bin 0 -> 621 bytes .../ic_replay_white_24dp_png.png | Bin 0 -> 411 bytes .../ic_replay_white_24dp_png.png | Bin 0 -> 837 bytes .../ic_replay_white_24dp_png.png | Bin 0 -> 1420 bytes .../ic_replay_white_24dp_png.png | Bin 0 -> 2098 bytes 6 files changed, 10 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png create mode 100644 app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 1656d60b1..370631116 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -257,18 +257,6 @@ public final class NotificationUtil { R.string.exo_controls_fastforward_description, ACTION_FAST_FORWARD); } - case NotificationConstants.PLAY_PAUSE: - if (player.isPlaying() - || player.getCurrentState() == BasePlayer.STATE_PREFLIGHT - || player.getCurrentState() == BasePlayer.STATE_BLOCKED - || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { - return getAction(player, R.drawable.exo_notification_pause, - R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); - } else { - return getAction(player, R.drawable.exo_notification_play, - R.string.exo_controls_play_description, ACTION_PLAY_PAUSE); - } - case NotificationConstants.PLAY_PAUSE_BUFFERING: if (player.getCurrentState() == BasePlayer.STATE_PREFLIGHT || player.getCurrentState() == BasePlayer.STATE_BLOCKED @@ -277,7 +265,16 @@ public final class NotificationUtil { return new NotificationCompat.Action(R.drawable.ic_hourglass_top_white_24dp_png, player.context.getString(R.string.notification_action_buffering), null); - } else if (player.isPlaying()) { + } + + case NotificationConstants.PLAY_PAUSE: + if (player.getCurrentState() == BasePlayer.STATE_COMPLETED) { + return getAction(player, R.drawable.ic_replay_white_24dp_png, + R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); + } else if (player.isPlaying() + || player.getCurrentState() == BasePlayer.STATE_PREFLIGHT + || player.getCurrentState() == BasePlayer.STATE_BLOCKED + || player.getCurrentState() == BasePlayer.STATE_BUFFERING) { return getAction(player, R.drawable.exo_notification_pause, R.string.exo_controls_pause_description, ACTION_PLAY_PAUSE); } else { diff --git a/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..8c5afb38015ca1370dc2a166c21a5bb27e198402 GIT binary patch literal 621 zcmV-z0+RiSP)%N_m)1RvbM11YEqd0VQ&)pg@PH6kvTtPz=bRTp9uA z%J+w$T!*N1u_O#iDcvN_95QEEm><@gs)y`NGID$OO&$ zf}lnwXv2tO2_w=(K^7+H%!uP9bF4H`P%Yc{*ofmUb8IkCP$%2hYQ%AdIgXk!LeK~^ z!S?;>?k_n2(IyCrqjAJ|ju}6-vDF*yA!IH8^zbEG&p&XsGBbif_r2el|Qv$tW z|5HZUaS-ZhGA4p)J9F|r7y~V!8q|PR@SSo_wMh_bRzr&dLk`}_9N9JaDrX`cmtzGr zO`8isr1&ZDN%xH%0*b*GO7eYrxQxUem;*ha2z>bO{{VXiYf$hjFY7I100000NkvXX Hu0mjfQxF~R literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png new file mode 100644 index 0000000000000000000000000000000000000000..038804d113396e8fd7adb1d3e8b4de356df7b8fb GIT binary patch literal 411 zcmV;M0c8G(P)6FMNo_4fcPiWl5R?pYB#QY02Hr+TJRa!O|uYn2!p&I@}y}kxoB-lW40uZkU;@?m;?||5w)Kp;t#3!+orpN$P z&>aQ&lHAM#Doy=?co7ia1>$c|d*`POTrs{B+*5yx=gQ7EE4_HxZu}H+=G}3C zjlZn^Er-A)K4Qbcz-%f~>5M1&3|@kEPOZnmYETRYC1w0ba8*TO+rY4tx2TVncz-92Xet|@IpB8gbHR7PR#Pv2`|HUD8Sci((CS|s&yr33)0Nt z*`eTpaDtLUev+eQb-;IO!B?LHJz$MfmTN<+iQ^7mYPHYUTtcqvWpYjE)joE>EQHjZ!%CO34=;#!mllq&2n2aw3 z&%}Bykw#3syhj`(k`1OuJ~9$)6YJ2I*c4Uq1n^iWru4edaQm=aoPEVvu*8J&&Ei}t z9^@+w89zreQ&YUvn%W_{Nw}z)TWRuI6!tjrv00{>0Z2~MU8?EplTOV-uQ( z4r~DjxX`0G`hSDXp1p+bZTRXV72#61gR5^*QsZA4 zb*UWSy%{(|fB(R>WhWSyEmPDGuoUd&(VPPX7oir?3Q~L$1ug=QOwd;3M!VcpgjugF(raB1~IwANW4y`ViF` zD6B--_2dNj6g&xfgHjpG#p}cU5q(ZJ;vEB@fX6{Mmjr4LmIb-9zrk`a3UqemNSDd} zltYnZEB*@J;h|7$yGU1oN5Idm@Il$wd9WKihe-|vJ0l9T2$bTiBKo9c$TO}6J;7+O7#z%C z`<@0%Kvx&yqKD}pS{+H_=Uupc<+MDN=2?G%S)hXpBFRen(Zp?@@z%9`W=)!BN+>Ya z`qPacOoGVmX|_i);xlh!(*LaHnZHX2;7Tw#B#q;aDo`8H3w!`-HP2n|L@=4+xObVn z&ExNG1#7?s#dGB@H_@dF?Sl%(ovlDc%pmMiY+>0>9WI~|Z!7mDd{@`7)jcXzeXPjh0AW6Ht&}{3b z=9A`HA3Q1?w}|wpq2~?cl_bo*a4OLjli*#M+YYl@X9CBVSLAGPP9f5?M4=m#XDVmC z&uMlXD>A|Kb7-F$Noxv)FK);zDOqELbSW=>v z3UobKrA?;t23jj+U`qw+!DL#PZ81HVs%`E|nTVwVN&oe?ig~XzJ=jXcKs#)W zN}1f>tC;r^(}TUD$@h-8G~YBuQ~8)>dcY?X1IdkfKImwfK-Yn{6nP;@%MLX?pi~cz zDnyc0W>3omx`RDRVYb`2-(a#X5bMEjC`^VAsY}O(Vf)5_ec4 zPhrzJo@Le-qfJn4jc^ zUIv}w8eGaM4`^1AoDOe-Yw{#c30v}1U!W8Q?umO6#Vo7TY1&Sbv3*PWWBqmTuO`hp zH&>I9vP!k44la#O&*rKYPI2b2J56Ojk8WKT?1H@1!I(~1t8t1=-KSDoevD4E$bb*L0Ze8aO}%F9 zdUm4auLg#}?_}P$KFw-6$Mok&3fnQ_mX?mPTi7jDnPz)8eKC?O>z(hQOuo@!8H&_& z%1LE&9vH)xl`_Uy?fA_0TqQSyyLnMw$T4a&7(>MSoGlzdo#V(E5_-)c^87bRacjxIgli_|{j)XbO6viam z@=`jd+J`U2RcGj%vat&kM*LY>!Zo9Bs^GTUZ*WD)iMR*6Mn_zfaMie)O4vmvIpTNu z`cgKpJ=IX`rzuRQ#Fb=Bx|Q;k%CU5)D;U?vhd+}NfmidN5G-dTJc|9$CCCKxr amHh{XE}6516NZ%l00003$g6rQ$>ZD}bTgkl7(C_$rj#RZKUS``FQiHO>2RV2U<{h`JX8Lf*U4E zG@=j}P>2XBQlLsz6aAuK0MwAyfA`L`0jARHl-TaCD@{Go&ugnz_wu>`oJ z_zc30P=1~GUn~JGC_kHU4dF9Fvp7*K0h*PcOSqo!p=e($0h*ORnDD;nSS$gWl%GSm zL85$1fF|W$e5o%%yNV4)r5WW@aVhKp1^6w;65E?B3?kj&JVJ)Fv zI1@`iCl#L#v$KwHR5%w)Kqn*ITVV5>MPgzJNKpBCNUSXrofAty0?N-P40GUW#*+^S zqwsm0thjssC7YL<nu5D0h}cG`==0pMLmn2xSPkye3VAzX_irD9kdIgb3k&!Wxf03G7jQM|grz zU@8ec2_=Np9wmVjfm;Z(2zsC_-QM0VMG1H50%Q^UZ{v5&{k)O_32K63SqX z>UB0|(P#G|JSWMb9z#j9cPL1L${Ld}Nwe8ImvC6}6mU9YL?~!NUwSl8%xk_z1O=XW z?}7t_=R;Ps5WsHf7>OBbhD+MTYApE#aJ}LTg|zh^4&toF^pFrn>Y-8M4c%k4rH=%T z32NgIP{ZvkR#W;2cv6z2V{&F%P3a8)4@lAy^R1@z5inFzu;KK`600eF1PnxLd%Pk| zrJIL|hw}|2fV&}fNxCMsyLA{@huI%o<}-|d)4fE%9!X%b)3kaidWmEvsKTwoR7rYw zc5_RhVFYkhZl9!QXt7&|StglkXK$g{FapM)!8hJ)!92GP^G=(^dF^EfOARC7X>?1+ z3&f_nb(on7VfhTh2zU>7c}K$JOmOQkk4f$Zej%+jMDb}TPyR01XHo51Q3hKK_e+Kw zYT>$N_?&<+dWD)K-A&Ccy{5n;FCgH0k9v{nQ*WGS3vVO17xHJ`OW72$h_xD4e zsBChE=L@=};*bktwwQM$`*0V9eMUf6?6+u8_R&?0z3t>R_;TX+s*Td@_Q2=S6Cu=7%l2KsXAx|awJs>L(l867c}f~<>%sN*{pE) zv+ch4!k37j|${es4{%OK=- zY7;oyEZ=NZglpOzBScLJ60sDij-pO_H=I(K2;cs20%vK!)~rn7r-UL%D8!>)lfcfl z2K>L>7=oLLm;7LC!Rp((UJ{A3LMZzt%#Y2%p3=Qge51onS^te5`F1@`3<=LD)DGf# z9*ywN`6alGun85p4H%r@e>Wn1unhy&)p+`%30`v(YTtc)zZ(c1&Ul!lM1QEkEvd&Q z#!{oY`{1IEhCQwEsPfr;=GFJZJQL+6nS?wD9x+IDlfFs-4czFlsHw8dM1fC7Y48P(|Y|*UvW@KeIBKgMVrke=z&zwL3<6(c- zqYJG%Nl!);QTcwXsjNUEu*AqktVv33kHqS*bx>RlqSrda2TgcVqA^Nt0bxh+ceN;m za<1na#1TURw^XB(WaJ>|8i!b7J|g5w#02#*d79R{n}5XTHF12Q_e0qGlV!DO#flXx cR=g(v0->loY0Q31%>V!Z07*qoM6N<$g1M5<6951J literal 0 HcmV?d00001 From ad3364671d48ea697f40dcb31ca5d72232716656 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 22 Sep 2020 15:45:40 +0200 Subject: [PATCH 27/30] Add migration concept for shared preferences --- .../org/schabi/newpipe/report/UserAction.java | 3 +- .../newpipe/settings/NewPipeSettings.java | 19 ++++ .../newpipe/settings/SettingMigrations.java | 106 ++++++++++++++++++ app/src/main/res/values/settings_keys.xml | 4 + 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java diff --git a/app/src/main/java/org/schabi/newpipe/report/UserAction.java b/app/src/main/java/org/schabi/newpipe/report/UserAction.java index faa5e7a44..6fa697f71 100644 --- a/app/src/main/java/org/schabi/newpipe/report/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/report/UserAction.java @@ -20,7 +20,8 @@ public enum UserAction { DELETE_FROM_HISTORY("delete from history"), PLAY_STREAM("Play stream"), DOWNLOAD_POSTPROCESSING("download post-processing"), - DOWNLOAD_FAILED("download failed"); + DOWNLOAD_FAILED("download failed"), + PREFERENCES_MIGRATION("migration of preferences"); private final String message; diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 8ce5fe4c2..aaf2077d2 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -10,6 +10,7 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import java.io.File; +import java.util.Set; /* * Created by k3b on 07.01.2016. @@ -38,6 +39,22 @@ public final class NewPipeSettings { private NewPipeSettings() { } public static void initSettings(final Context context) { + // check if there are entries in the prefs to determine whether this is the first app run + Boolean isFirstRun = null; + final Set prefsKeys = PreferenceManager.getDefaultSharedPreferences(context) + .getAll().keySet(); + for (final String key: prefsKeys) { + // ACRA stores some info in the prefs during app initialization + // which happens before this method is called. Therefore ignore ACRA-related keys. + if (!key.toLowerCase().startsWith("acra")) { + isFirstRun = false; + break; + } + } + if (isFirstRun == null) { + isFirstRun = true; + } + PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true); PreferenceManager.setDefaultValues(context, R.xml.content_settings, true); PreferenceManager.setDefaultValues(context, R.xml.download_settings, true); @@ -48,6 +65,8 @@ public final class NewPipeSettings { getVideoDownloadFolder(context); getAudioDownloadFolder(context); + + SettingMigrations.initMigrations(context, isFirstRun); } private static void getVideoDownloadFolder(final Context context) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java new file mode 100644 index 000000000..5ef92fc25 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -0,0 +1,106 @@ +package org.schabi.newpipe.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import androidx.preference.PreferenceManager; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.ErrorActivity.ErrorInfo; +import org.schabi.newpipe.report.UserAction; + +import static org.schabi.newpipe.MainActivity.DEBUG; + +public final class SettingMigrations { + private static final String TAG = SettingMigrations.class.toString(); + /** + * Version number for preferences. Must be incremented every time a migration is necessary. + */ + public static final int VERSION = 0; + private static SharedPreferences sp; + + /** + * List of all implemented migrations. + *

+ * Append new migrations to the end of the list to keep it sorted ascending. + * If not sorted correctly, migrations which depend on each other, may fail. + */ + private static final Migration[] SETTING_MIGRATIONS = { + + }; + + + public static void initMigrations(final Context context, final boolean isFirstRun) { + // setup migrations and check if there is something to do + sp = PreferenceManager.getDefaultSharedPreferences(context); + final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); + final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); + + // no migration to run, already up to date + if (isFirstRun) { + sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); + return; + } else if (lastPrefVersion == VERSION) { + return; + } + + // run migrations + int currentVersion = lastPrefVersion; + for (final Migration currentMigration : SETTING_MIGRATIONS) { + try { + if (currentMigration.shouldMigrate(currentVersion)) { + if (DEBUG) { + Log.d(TAG, "Migrating preferences from version " + + currentVersion + " to " + currentMigration.newVersion); + } + currentMigration.migrate(context); + currentVersion = currentMigration.newVersion; + } + } catch (final Exception e) { + // save the version with the last successful migration and report the error + sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); + final ErrorInfo errorInfo = ErrorInfo.make( + UserAction.PREFERENCES_MIGRATION, + "none", + "Migrating preferences from version " + lastPrefVersion + " to " + + VERSION + ". " + + "Error at " + currentVersion + " => " + ++currentVersion, + 0 + ); + ErrorActivity.reportError(context, e, SettingMigrations.class, null, errorInfo); + return; + } + } + + // store the current preferences version + sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); + } + + private SettingMigrations() { } + + abstract static class Migration { + public final int oldVersion; + public final int newVersion; + + protected Migration(final int oldVersion, final int newVersion) { + this.oldVersion = oldVersion; + this.newVersion = newVersion; + } + + /** + * @param currentVersion current settings version + * @return Returns whether this migration should be run. + * A migration is necessary if the old version of this migration is lower than or equal to + * the current settings version. + */ + private boolean shouldMigrate(final int currentVersion) { + return oldVersion >= currentVersion; + } + + protected abstract void migrate(Context context); + + } + +} diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 88371f6c4..39f48a951 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1,5 +1,9 @@ + + last_used_version + last_used_preferences_version + @string/youtube From 0e5f85db95b9aed6ad824db591a354633b67319d Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 9 Sep 2020 20:45:42 +0200 Subject: [PATCH 28/30] Remove "Detail Page" open action from share dialog under certain circumstances With the new application workflow and unified player, video detail page and video player are the same activity. So show only one of these options based on whether autoplay is enabled or not, and show both if using external player --- .../org/schabi/newpipe/RouterActivity.java | 59 ++++++++++++++----- .../fragments/detail/VideoDetailFragment.java | 20 +------ .../newpipe/player/helper/PlayerHelper.java | 13 ++++ .../newpipe/settings/SettingMigrations.java | 18 +++++- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 12fdf8c78..03f232c13 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -39,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; @@ -278,6 +279,7 @@ public class RouterActivity extends AppCompatActivity { handleChoice(choice.key); + // open future streams always like this one, because "always" button was used by user if (which == DialogInterface.BUTTON_POSITIVE) { preferences.edit() .putString(getString(R.string.preferred_open_action_key), choice.key) @@ -377,23 +379,50 @@ public class RouterActivity extends AppCompatActivity { final boolean isExtAudioEnabled = preferences.getBoolean( getString(R.string.use_external_audio_player_key), false); - returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), - getString(R.string.show_info), - resolveResourceIdFromAttr(context, R.attr.ic_info_outline))); + final AdapterChoiceItem videoPlayer = new AdapterChoiceItem( + getString(R.string.video_player_key), getString(R.string.video_player), + resolveResourceIdFromAttr(context, R.attr.ic_play_arrow)); + final AdapterChoiceItem showInfo = new AdapterChoiceItem( + getString(R.string.show_info_key), getString(R.string.show_info), + resolveResourceIdFromAttr(context, R.attr.ic_info_outline)); + final AdapterChoiceItem popupPlayer = new AdapterChoiceItem( + getString(R.string.popup_player_key), getString(R.string.popup_player), + resolveResourceIdFromAttr(context, R.attr.ic_popup)); + final AdapterChoiceItem backgroundPlayer = new AdapterChoiceItem( + getString(R.string.background_player_key), getString(R.string.background_player), + resolveResourceIdFromAttr(context, R.attr.ic_headset)); - if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) { - returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), - getString(R.string.video_player), - resolveResourceIdFromAttr(context, R.attr.ic_play_arrow))); - returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), - getString(R.string.popup_player), - resolveResourceIdFromAttr(context, R.attr.ic_popup))); - } + if (linkType == LinkType.STREAM) { + if (isExtVideoEnabled) { + // show both "show info" and "video player", they are two different activities + returnList.add(showInfo); + returnList.add(videoPlayer); + } else if (capabilities.contains(VIDEO) + && PlayerHelper.isAutoplayAllowedByUser(context)) { + // show only "video player" since the details activity will be opened and the video + // will be autoplayed there and "show info" would do the exact same thing + returnList.add(videoPlayer); + } else { + // show only "show info" if video player is not applicable or autoplay is disabled + returnList.add(showInfo); + } - if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) { - returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), - getString(R.string.background_player), - resolveResourceIdFromAttr(context, R.attr.ic_headset))); + if (capabilities.contains(VIDEO)) { + returnList.add(popupPlayer); + } + if (capabilities.contains(AUDIO)) { + returnList.add(backgroundPlayer); + } + + } else { + returnList.add(showInfo); + if (capabilities.contains(VIDEO) && !isExtVideoEnabled) { + returnList.add(videoPlayer); + returnList.add(popupPlayer); + } + if (capabilities.contains(AUDIO) && !isExtAudioEnabled) { + returnList.add(backgroundPlayer); + } } returnList.add(new AdapterChoiceItem(getString(R.string.download_key), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 506e8f815..eb889cb00 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1236,7 +1236,7 @@ public class VideoDetailFragment } private boolean isExternalPlayerEnabled() { - return PreferenceManager.getDefaultSharedPreferences(getContext()) + return PreferenceManager.getDefaultSharedPreferences(requireContext()) .getBoolean(getString(R.string.use_external_video_player_key), false); } @@ -1247,23 +1247,7 @@ public class VideoDetailFragment && !isExternalPlayerEnabled() && (player == null || player.videoPlayerSelected()) && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN - && isAutoplayAllowedByUser(); - } - - private boolean isAutoplayAllowedByUser() { - if (activity == null) { - return false; - } - - switch (PlayerHelper.getAutoplayType(activity)) { - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER: - return false; - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI: - return !ListHelper.isMeteredNetwork(activity); - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS: - default: - return true; - } + && PlayerHelper.isAutoplayAllowedByUser(requireContext()); } private void addVideoPlayerView() { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 6667d0ecb..fd59e1d99 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -28,6 +28,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.util.ListHelper; import java.lang.annotation.Retention; import java.text.DecimalFormat; @@ -248,6 +249,18 @@ public final class PlayerHelper { } } + public static boolean isAutoplayAllowedByUser(@NonNull final Context context) { + switch (PlayerHelper.getAutoplayType(context)) { + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER: + return false; + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI: + return !ListHelper.isMeteredNetwork(context); + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS: + default: + return true; + } + } + @NonNull public static SeekParameters getSeekParameters(@NonNull final Context context) { return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index 5ef92fc25..f2896f078 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -18,9 +18,23 @@ public final class SettingMigrations { /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - public static final int VERSION = 0; + public static final int VERSION = 1; private static SharedPreferences sp; + public static final Migration MIGRATION_0_1 = new Migration(0, 1) { + @Override + public void migrate(final Context context) { + // We changed the content of the dialog which opens when sharing a link to NewPipe + // by removing the "open detail page" option. + // Therefore, show the dialog once again to ensure users need to choose again and are + // aware of the changed dialog. + final SharedPreferences.Editor editor = sp.edit(); + editor.putString(context.getString(R.string.preferred_open_action_key), + context.getString(R.string.always_ask_open_action_key)); + editor.apply(); + } + }; + /** * List of all implemented migrations. *

@@ -28,7 +42,7 @@ public final class SettingMigrations { * If not sorted correctly, migrations which depend on each other, may fail. */ private static final Migration[] SETTING_MIGRATIONS = { - + MIGRATION_0_1 }; From 3c4a4e53840bf5bfe91d1ec1492ab22ab7ddf29a Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 9 Sep 2020 21:08:17 +0200 Subject: [PATCH 29/30] Set default value for "minimize_on_exit" to background for better UX. --- .../newpipe/settings/SettingMigrations.java | 24 +++++++++++++++++-- app/src/main/res/values/settings_keys.xml | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index f2896f078..26e72722e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -18,7 +18,7 @@ public final class SettingMigrations { /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - public static final int VERSION = 1; + public static final int VERSION = 2; private static SharedPreferences sp; public static final Migration MIGRATION_0_1 = new Migration(0, 1) { @@ -35,6 +35,25 @@ public final class SettingMigrations { } }; + public static final Migration MIGRATION_1_2 = new Migration(1, 2) { + @Override + protected void migrate(final Context context) { + // The new application workflow introduced in #2907 allows minimizing videos + // while playing to do other stuff within the app. + // For an even better workflow, we minimize a stream when switching the app to play in + // background. + // Therefore, set default value to background, if it has not been changed yet. + final String minimizeOnExitKey = context.getString(R.string.minimize_on_exit_key); + if (sp.getString(minimizeOnExitKey, "") + .equals(context.getString(R.string.minimize_on_exit_none_key))) { + final SharedPreferences.Editor editor = sp.edit(); + editor.putString(minimizeOnExitKey, + context.getString(R.string.minimize_on_exit_background_key)); + editor.apply(); + } + } + }; + /** * List of all implemented migrations. *

@@ -42,7 +61,8 @@ public final class SettingMigrations { * If not sorted correctly, migrations which depend on each other, may fail. */ private static final Migration[] SETTING_MIGRATIONS = { - MIGRATION_0_1 + MIGRATION_0_1, + MIGRATION_1_2 }; diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 39f48a951..bddad9dae 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -56,7 +56,7 @@ minimize_on_exit_key - @string/minimize_on_exit_none_key + @string/minimize_on_exit_background_key minimize_on_exit_none_key minimize_on_exit_background_key minimize_on_exit_popup_key From b53d5d8c00d54c479bc299a64f0707deb1033550 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 26 Sep 2020 22:32:11 +0200 Subject: [PATCH 30/30] Allow numbers and uppercase letters in app package id --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 80171be62..95d35eeac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,7 +33,7 @@ android { // suffix the app id and the app name with git branch name def workingBranch = getGitWorkingBranch() - def normalizedWorkingBranch = workingBranch.replaceAll("[^A-Za-z]+", "").toLowerCase() + def normalizedWorkingBranch = workingBranch.replaceFirst("^[^A-Za-z]+", "").replaceAll("[^0-9A-Za-z]+", "") if (normalizedWorkingBranch.isEmpty() || workingBranch == "master" || workingBranch == "dev") { // default values when branch name could not be determined or is master or dev applicationIdSuffix ".debug"