Merge branch 'dev' into dev
This commit is contained in:
commit
e0a39efa2b
31 changed files with 1130 additions and 204 deletions
|
|
@ -559,8 +559,16 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
|||
case R.id.audio_button:
|
||||
mainStorage = mainStorageAudio;
|
||||
format = audioStreamsAdapter.getItem(selectedAudioIndex).getFormat();
|
||||
mime = format.mimeType;
|
||||
filename += format.suffix;
|
||||
switch(format) {
|
||||
case WEBMA_OPUS:
|
||||
mime = "audio/ogg";
|
||||
filename += "opus";
|
||||
break;
|
||||
default:
|
||||
mime = format.mimeType;
|
||||
filename += format.suffix;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case R.id.video_button:
|
||||
mainStorage = mainStorageVideo;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
package org.schabi.newpipe.local.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
|
|
@ -152,6 +153,12 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
final Toast successToast = Toast.makeText(getContext(),
|
||||
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
|
||||
|
||||
if (playlist.thumbnailUrl.equals("drawable://" + R.drawable.dummy_thumbnail_playlist)) {
|
||||
playlistDisposables.add(manager.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> successToast.show()));
|
||||
}
|
||||
|
||||
playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> successToast.show()));
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import android.app.Activity;
|
|||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -18,6 +13,12 @@ import android.widget.EditText;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
|
|
@ -413,10 +414,25 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
disposables.add(disposable);
|
||||
}
|
||||
|
||||
private void updateThumbnailUrl() {
|
||||
String newThumbnailUrl;
|
||||
|
||||
if (!itemListAdapter.getItemsList().isEmpty()) {
|
||||
newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0)).thumbnailUrl;
|
||||
} else {
|
||||
newThumbnailUrl = "drawable://" + R.drawable.dummy_thumbnail_playlist;
|
||||
}
|
||||
|
||||
changeThumbnailUrl(newThumbnailUrl);
|
||||
}
|
||||
|
||||
private void deleteItem(final PlaylistStreamEntry item) {
|
||||
if (itemListAdapter == null) return;
|
||||
|
||||
itemListAdapter.removeItem(item);
|
||||
if (playlistManager.getPlaylistThumbnail(playlistId).equals(item.thumbnailUrl))
|
||||
updateThumbnailUrl();
|
||||
|
||||
setVideoCount(itemListAdapter.getItemsList().size());
|
||||
saveChanges();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ public class LocalPlaylistManager {
|
|||
return modifyPlaylist(playlistId, null, thumbnailUrl);
|
||||
}
|
||||
|
||||
public String getPlaylistThumbnail(final long playlistId) {
|
||||
return playlistTable.getPlaylist(playlistId).blockingFirst().get(0).getThumbnailUrl();
|
||||
}
|
||||
|
||||
private Maybe<Integer> modifyPlaylist(final long playlistId,
|
||||
@Nullable final String name,
|
||||
@Nullable final String thumbnailUrl) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
43
app/src/main/java/org/schabi/newpipe/util/BitmapUtils.java
Normal file
43
app/src/main/java/org/schabi/newpipe/util/BitmapUtils.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -140,7 +140,15 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
|
|||
if (stream instanceof SubtitlesStream) {
|
||||
formatNameView.setText(((SubtitlesStream) stream).getLanguageTag());
|
||||
} else {
|
||||
formatNameView.setText(stream.getFormat().getName());
|
||||
switch (stream.getFormat()) {
|
||||
case WEBMA_OPUS:
|
||||
// noinspection AndroidLintSetTextI18n
|
||||
formatNameView.setText("opus");
|
||||
break;
|
||||
default:
|
||||
formatNameView.setText(stream.getFormat().getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qualityView.setText(qualityString);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue