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 90af3c29f..76da7da36 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
@@ -25,12 +25,17 @@ 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
+
+import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -48,6 +53,7 @@ import org.schabi.newpipe.player.helper.LockManager;
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;
@@ -75,6 +81,7 @@ public final class BackgroundPlayer extends Service {
private BasePlayerImpl basePlayerImpl;
private LockManager lockManager;
+ private SharedPreferences sharedPreferences;
/*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder
@@ -107,6 +114,7 @@ public final class BackgroundPlayer extends Service {
if (DEBUG) Log.d(TAG, "onCreate() called");
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
lockManager = new LockManager(this);
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
ThemeHelper.setTheme(this);
basePlayerImpl = new BasePlayerImpl(this);
@@ -199,12 +207,45 @@ public final class BackgroundPlayer extends Service {
.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(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() {
+ int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
+ int screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
+
+ return BitmapUtils.centerCrop(
+ basePlayerImpl.getThumbnail(),
+ screenWidth,
+ screenHeight);
+ }
+
private void setupNotification(RemoteViews remoteViews) {
if (basePlayerImpl == null) return;
@@ -252,8 +293,10 @@ public final class BackgroundPlayer extends Service {
//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);
+ 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++;
@@ -280,7 +323,8 @@ public final class BackgroundPlayer extends Service {
protected class BasePlayerImpl extends BasePlayer {
- @NonNull final private AudioPlaybackResolver resolver;
+ @NonNull
+ final private AudioPlaybackResolver resolver;
private int cachedDuration;
private String cachedDurationString;
@@ -299,8 +343,10 @@ public final class BackgroundPlayer extends Service {
super.handleIntent(intent);
resetNotification();
- if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
- if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
+ if (bigNotRemoteView != null)
+ bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
+ if (notRemoteView != null)
+ notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
startForeground(NOTIFICATION_ID, notBuilder.build());
}
@@ -335,6 +381,7 @@ public final class BackgroundPlayer extends Service {
updateNotificationThumbnail();
updateNotification(-1);
}
+
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@@ -358,10 +405,13 @@ public final class BackgroundPlayer extends Service {
if (!shouldUpdateOnProgress) return;
if (timesNotificationUpdated > NOTIFICATION_UPDATES_BEFORE_RESET) {
resetNotification();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /*Oreo*/) updateNotificationThumbnail();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /*Oreo*/) {
+ updateNotificationThumbnail();
+ }
}
if (bigNotRemoteView != null) {
- if(cachedDuration != duration) {
+ if (cachedDuration != duration) {
cachedDuration = duration;
cachedDurationString = getTimeString(duration);
}
@@ -389,8 +439,10 @@ 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);
+ if (notRemoteView != null)
+ notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
+ if (bigNotRemoteView != null)
+ bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, null);
}
/*//////////////////////////////////////////////////////////////////////////
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 a5c703837..8b9369613 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
@@ -2,12 +2,19 @@ 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.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
import androidx.media.session.MediaButtonReceiver;
+import androidx.media.app.NotificationCompat.MediaStyle;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
@@ -19,8 +26,10 @@ import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
public class MediaSessionManager {
private static final String TAG = "MediaSessionManager";
- @NonNull private final MediaSessionCompat mediaSession;
- @NonNull private final MediaSessionConnector sessionConnector;
+ @NonNull
+ private final MediaSessionCompat mediaSession;
+ @NonNull
+ private final MediaSessionConnector sessionConnector;
public MediaSessionManager(@NonNull final Context context,
@NonNull final Player player,
@@ -40,13 +49,45 @@ public class MediaSessionManager {
return MediaButtonReceiver.handleIntent(mediaSession, intent);
}
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public void setLockScreenArt(NotificationCompat.Builder builder, @Nullable Bitmap thumbnailBitmap) {
+ if (thumbnailBitmap == null || !mediaSession.isActive()) {
+ return;
+ }
+
+ mediaSession.setMetadata(
+ new MediaMetadataCompat.Builder()
+ .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, thumbnailBitmap)
+ .build()
+ );
+
+ MediaStyle mediaStyle = new MediaStyle()
+ .setMediaSession(mediaSession.getSessionToken());
+
+ builder.setStyle(mediaStyle);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public void clearLockScreenArt(NotificationCompat.Builder builder) {
+ mediaSession.setMetadata(
+ new MediaMetadataCompat.Builder()
+ .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null)
+ .build()
+ );
+
+ MediaStyle mediaStyle = new MediaStyle()
+ .setMediaSession(mediaSession.getSessionToken());
+
+ builder.setStyle(mediaStyle);
+ }
+
/**
* 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();
- }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/BitmapUtils.java b/app/src/main/java/org/schabi/newpipe/util/BitmapUtils.java
new file mode 100644
index 000000000..7ad71eb5c
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/BitmapUtils.java
@@ -0,0 +1,43 @@
+package org.schabi.newpipe.util;
+
+import android.graphics.Bitmap;
+
+import androidx.annotation.Nullable;
+
+public class BitmapUtils {
+
+ @Nullable
+ public static Bitmap centerCrop(Bitmap inputBitmap, int newWidth, int newHeight) {
+ if (inputBitmap == null || inputBitmap.isRecycled()) {
+ return null;
+ }
+
+ float sourceWidth = inputBitmap.getWidth();
+ float sourceHeight = inputBitmap.getHeight();
+
+ float xScale = newWidth / sourceWidth;
+ float yScale = newHeight / sourceHeight;
+
+ float newXScale;
+ float newYScale;
+
+ if (yScale > xScale) {
+ newXScale = xScale / yScale;
+ newYScale = 1.0f;
+ } else {
+ newXScale = 1.0f;
+ newYScale = yScale / xScale;
+ }
+
+ float scaledWidth = newXScale * sourceWidth;
+ float scaledHeight = newYScale * sourceHeight;
+
+ int left = (int) ((sourceWidth - scaledWidth) / 2);
+ int top = (int) ((sourceHeight - scaledHeight) / 2);
+ int width = (int) scaledWidth;
+ int height = (int) scaledHeight;
+
+ return Bitmap.createBitmap(inputBitmap, left, top, width, height);
+ }
+
+}
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index 6aaaa0630..4813833d1 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -175,6 +175,7 @@
main_page_content
enable_playback_resume
enable_playback_state_lists
+ enable_lock_screen_video_thumbnail
import_data
export_data
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6021df15e..a296e2db7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -58,7 +58,9 @@
Kore app not found. Install it?
org.xbmc.kore
Show \"Play with Kodi\" option
+ Enable lock screen video thumbnail
Display an option to play a video via Kodi media center
+ When using the background player a video thumbnail will be displayed on the lock screen
Audio
Default audio format
Default video format
diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml
index 0ff43ce90..d1757919b 100644
--- a/app/src/main/res/xml/video_audio_settings.xml
+++ b/app/src/main/res/xml/video_audio_settings.xml
@@ -81,6 +81,13 @@
android:summary="@string/show_play_with_kodi_summary"
android:title="@string/show_play_with_kodi_title"/>
+
+