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 <wb9688@users.noreply.github.com>
This commit is contained in:
cool-student 2020-03-03 00:35:44 +01:00 committed by wb9688
parent f290b2bf5a
commit 4abf6b2f5c
19 changed files with 2025 additions and 481 deletions

View File

@ -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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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) {

View File

@ -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);
}
/**

View File

@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -96,6 +96,113 @@
<item>144p</item>
</string-array>
<string name="enable_old_notifications_key" translatable="false">enable_old_notifications</string>
<string name="settings_notifications_compact_view_key" translatable="false">notifications_compact_view</string>
<string name="settings_notifications_compact_view_default_value" translatable="false">0,1,2</string>
<string name="scale_to_square_image_in_notifications_key" translatable="false">scale_to_square_image_in_notifications</string>
<string name="notification_slot_prev_key" translatable="false">prev</string>
<string name="notification_slot_next_key" translatable="false">next</string>
<string name="notification_slot_rewind_key" translatable="false">rewind</string>
<string name="notification_slot_forward_key" translatable="false">forward</string>
<string name="notification_slot_smart_rewind_prev_key" translatable="false">smart_rewind_prev</string>
<string name="notification_slot_smart_forward_next_key" translatable="false">smart_forward_next</string>
<string name="notification_slot_play_pause_buffering_key" translatable="false">play_pause_buffering</string>
<string name="notification_slot_play_pause_key" translatable="false">play_pause</string>
<string name="notification_slot_repeat_key" translatable="false">repeat</string>
<string name="notification_slot_shuffle_key" translatable="false">shuffle</string>
<string name="notification_slot_close_key" translatable="false">close</string>
<string name="notification_slot_n_a_key" translatable="false">n/a</string>
<string name="notification_slot_0_key" translatable="false">notification_slot_0_key</string>
<string name="notification_slot_0_value" translatable="false">@string/notification_slot_smart_rewind_prev_key</string>
<string-array name="notification_slot_0_description_list" translatable="false">
<item>Rewind / Previous</item>
<item>Previous</item>
<item>Rewind</item>
</string-array>
<string-array name="notification_slot_0_values_list" translatable="false">
<item>@string/notification_slot_smart_rewind_prev_key</item>
<item>@string/notification_slot_prev_key</item>
<item>@string/notification_slot_rewind_key</item>
</string-array>
<string name="notification_slot_1_key" translatable="false">notification_slot_1_key</string>
<string name="notification_slot_1_value" translatable="false">@string/notification_slot_play_pause_buffering_key</string>
<string-array name="notification_slot_1_description_list" translatable="false">
<item>Play / Pause / Buffering</item>
<item>Play / Pause</item>
<item>Rewind</item>
</string-array>
<string-array name="notification_slot_1_values_list" translatable="false">
<item>@string/notification_slot_play_pause_buffering_key</item>
<item>@string/notification_slot_play_pause_key</item>
<item>@string/notification_slot_rewind_key</item>
</string-array>
<string name="notification_slot_2_key" translatable="false">notification_slot_2_key</string>
<string name="notification_slot_2_value" translatable="false">@string/notification_slot_smart_forward_next_key</string>
<string-array name="notification_slot_2_description_list" translatable="false">
<item>Forward / Next</item>
<item>Forward</item>
<item>Next</item>
<item>Play / Pause / Buffering</item>
<item>Play / Pause</item>
</string-array>
<string-array name="notification_slot_2_values_list" translatable="false">
<item>@string/notification_slot_smart_forward_next_key</item>
<item>@string/notification_slot_forward_key</item>
<item>@string/notification_slot_next_key</item>
<item>@string/notification_slot_play_pause_buffering_key</item>
<item>@string/notification_slot_play_pause_key</item>
</string-array>
<string name="notification_slot_3_key" translatable="false">notification_slot_3_key</string>
<string name="notification_slot_3_value" translatable="false">@string/notification_slot_repeat_key</string>
<string-array name="notification_slot_3_description_list" translatable="false">
<item>Repeat</item>
<item>Shuffle</item>
<item>Previous</item>
<item>Forward</item>
<item>Forward / Next</item>
<item>Rewind</item>
<item>Rewind / Previous</item>
<item>Close</item>
<item>N/A</item>
</string-array>
<string-array name="notification_slot_3_values_list" translatable="false">
<item>@string/notification_slot_repeat_key</item>
<item>@string/notification_slot_shuffle_key</item>
<item>@string/notification_slot_prev_key</item>
<item>@string/notification_slot_forward_key</item>
<item>@string/notification_slot_smart_forward_next_key</item>
<item>@string/notification_slot_rewind_key</item>
<item>@string/notification_slot_smart_rewind_prev_key</item>
<item>@string/notification_slot_close_key</item>
<item>@string/notification_slot_n_a_key</item>
</string-array>
<string name="notification_slot_4_key" translatable="false">notification_slot_4_key</string>
<string name="notification_slot_4_value" translatable="false">@string/notification_slot_close_key</string>
<string-array name="notification_slot_4_description_list" translatable="false">
<item>Close</item>
<item>Repeat</item>
<item>Shuffle</item>
<item>Next</item>
<item>Forward</item>
<item>Forward / Next</item>
<item>N/A</item>
</string-array>
<string-array name="notification_slot_4_values_list" translatable="false">
<item>@string/notification_slot_close_key</item>
<item>@string/notification_slot_repeat_key</item>
<item>@string/notification_slot_shuffle_key</item>
<item>@string/notification_slot_next_key</item>
<item>@string/notification_slot_forward_key</item>
<item>@string/notification_slot_smart_forward_next_key</item>
<item>@string/notification_slot_n_a_key</item>
</string-array>
<string name="video_mp4_key" translatable="false">video_mp4</string>
<string name="video_webm_key" translatable="false">video_webm</string>

View File

@ -57,9 +57,18 @@
<string name="kore_not_found">Install missing Kore app?</string>
<string name="kore_package" translatable="false">org.xbmc.kore</string>
<string name="show_play_with_kodi_title">Show \"Play with Kodi\" option</string>
<string name="enable_lock_screen_video_thumbnail_title">Lock screen video thumbnail</string>
<string name="show_play_with_kodi_summary">Display an option to play a video via Kodi media center</string>
<string name="enable_lock_screen_video_thumbnail_summary">A video thumbnail is shown on the lock screen when using the background player</string>
<string name="enable_old_notifications_title">Enable old notifications</string>
<string name="enable_old_notifications_summary">This enables the old \"Custom RemoteViews\" notifications. If disabled modern MediaStyle notifications will be used.</string>
<string name="scale_to_square_image_in_notifications_title">Scale image to 1:1 aspect ratio</string>
<string name="scale_to_square_image_in_notifications_summary">This will scale the notification image from 16:9 to 1:1 aspect ratio</string>
<string name="default_notification_slot_0_title">Notification slot 0</string>
<string name="default_notification_slot_1_title">Notification slot 1</string>
<string name="default_notification_slot_2_title">Notification slot 2</string>
<string name="default_notification_slot_3_title">Notification slot 3</string>
<string name="default_notification_slot_4_title">Notification slot 4</string>
<string name="settings_notifications_compact_view_title">Notification compact view</string>
<string name="settings_notifications_compact_view_summary">Notification slots to show in compact view</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Default audio format</string>
<string name="default_video_format_title">Default video format</string>
@ -129,6 +138,7 @@
<string name="settings_category_other_title">Other</string>
<string name="settings_category_debug_title">Debug</string>
<string name="settings_category_updates_title">Updates</string>
<string name="settings_category_notifications_title">Notifications</string>
<string name="background_player_playing_toast">Playing in background</string>
<string name="popup_playing_toast">Playing in popup mode</string>
<string name="background_player_append">Queued on background player</string>

View File

@ -1,38 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/settings_category_appearance_title">
<ListPreference
app:iconSpaceReserved="false"
android:defaultValue="@string/default_theme_value"
android:entries="@array/theme_description_list"
android:entryValues="@array/theme_values_list"
android:key="@string/theme_key"
android:summary="%s"
android:title="@string/theme_title"/>
android:title="@string/theme_title"
app:iconSpaceReserved="false" />
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true"
android:key="@string/show_hold_to_append_key"
android:summary="@string/show_hold_to_append_summary"
android:title="@string/show_hold_to_append_title"
android:summary="@string/show_hold_to_append_summary"/>
app:iconSpaceReserved="false" />
<ListPreference
app:iconSpaceReserved="false"
android:defaultValue="@string/list_view_mode_value"
android:entries="@array/list_view_mode_description"
android:entryValues="@array/list_view_mode_values"
android:key="@string/list_view_mode_key"
android:summary="%s"
android:title="@string/list_view_mode"/>
android:title="@string/list_view_mode"
app:iconSpaceReserved="false" />
<Preference
app:iconSpaceReserved="false"
android:key="@string/caption_settings_key"
android:summary="@string/caption_setting_description"
android:title="@string/caption_setting_title"
android:summary="@string/caption_setting_description"/>
app:iconSpaceReserved="false" />
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_notifications_title"
app:iconSpaceReserved="false">
<SwitchPreference
android:defaultValue="false"
android:key="@string/enable_old_notifications_key"
android:summary="@string/enable_old_notifications_summary"
android:title="@string/enable_old_notifications_title"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/scale_to_square_image_in_notifications_key"
android:summary="@string/scale_to_square_image_in_notifications_summary"
android:title="@string/scale_to_square_image_in_notifications_title"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="@string/notification_slot_0_value"
android:entries="@array/notification_slot_0_description_list"
android:entryValues="@array/notification_slot_0_values_list"
android:key="@string/notification_slot_0_key"
android:summary="%s"
android:title="@string/default_notification_slot_0_title"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="@string/notification_slot_1_value"
android:entries="@array/notification_slot_1_description_list"
android:entryValues="@array/notification_slot_1_values_list"
android:key="@string/notification_slot_1_key"
android:summary="%s"
android:title="@string/default_notification_slot_1_title"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="@string/notification_slot_2_value"
android:entries="@array/notification_slot_2_description_list"
android:entryValues="@array/notification_slot_2_values_list"
android:key="@string/notification_slot_2_key"
android:summary="%s"
android:title="@string/default_notification_slot_2_title"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="@string/notification_slot_3_value"
android:entries="@array/notification_slot_3_description_list"
android:entryValues="@array/notification_slot_3_values_list"
android:key="@string/notification_slot_3_key"
android:summary="%s"
android:title="@string/default_notification_slot_3_title"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="@string/notification_slot_4_value"
android:entries="@array/notification_slot_4_description_list"
android:entryValues="@array/notification_slot_4_values_list"
android:key="@string/notification_slot_4_key"
android:summary="%s"
android:title="@string/default_notification_slot_4_title"
app:iconSpaceReserved="false" />
<EditTextPreference
android:defaultValue="@string/settings_notifications_compact_view_default_value"
android:key="@string/settings_notifications_compact_view_key"
android:summary="@string/settings_notifications_compact_view_summary"
android:title="@string/settings_notifications_compact_view_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -81,13 +81,6 @@
android:summary="@string/show_play_with_kodi_summary"
android:title="@string/show_play_with_kodi_title"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true"
android:key="@string/enable_lock_screen_video_thumbnail_key"
android:summary="@string/enable_lock_screen_video_thumbnail_summary"
android:title="@string/enable_lock_screen_video_thumbnail_title"/>
</PreferenceCategory>
<PreferenceCategory