Improve code formatting, annotate more fields and methods

Also simplify logic in MediaBrowserConnector.extractPlayQueueFromMediaId,
suppress some Sonar warnings as they could not be solved, and use explicit
types in some variables.
This commit is contained in:
AudricV 2023-08-11 13:22:43 +02:00 committed by Siddhesh Naik
parent fd0ca907aa
commit f4e5920156
4 changed files with 116 additions and 78 deletions

View File

@ -431,6 +431,6 @@
<meta-data android:name="com.google.android.gms.car.application" <meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" /> android:resource="@xml/automotive_app_desc" />
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon" <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@mipmap/ic_launcher" /> android:resource="@mipmap/ic_launcher" />
</application> </application>
</manifest> </manifest>

View File

@ -31,7 +31,12 @@ data class StreamHistoryEntry(
} }
fun toStreamInfoItem(): StreamInfoItem { fun toStreamInfoItem(): StreamInfoItem {
val item = StreamInfoItem(streamEntity.serviceId, streamEntity.url, streamEntity.title, streamEntity.streamType) val item = StreamInfoItem(
streamEntity.serviceId,
streamEntity.url,
streamEntity.title,
streamEntity.streamType
)
item.duration = streamEntity.duration item.duration = streamEntity.duration
item.uploaderName = streamEntity.uploader item.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl item.uploaderUrl = streamEntity.uploaderUrl

View File

@ -43,8 +43,10 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.Objects;
import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
/** /**
* One service for all players. * One service for all players.
@ -53,6 +55,7 @@ public final class PlayerService extends MediaBrowserServiceCompat {
private static final String TAG = PlayerService.class.getSimpleName(); private static final String TAG = PlayerService.class.getSimpleName();
private static final boolean DEBUG = Player.DEBUG; private static final boolean DEBUG = Player.DEBUG;
@Nullable
private Player player; private Player player;
private final IBinder mBinder = new PlayerService.LocalBinder(this); private final IBinder mBinder = new PlayerService.LocalBinder(this);
@ -79,7 +82,7 @@ public final class PlayerService extends MediaBrowserServiceCompat {
mediaBrowserConnector = new MediaBrowserConnector(this); mediaBrowserConnector = new MediaBrowserConnector(this);
} }
private void initializePlayer() { private void initializePlayerIfNeeded() {
if (player == null) { if (player == null) {
player = new Player(this); player = new Player(this);
/* /*
@ -93,6 +96,9 @@ public final class PlayerService extends MediaBrowserServiceCompat {
} }
} }
// Suppress Sonar warning to not always return the same value, as we need to do some actions
// before returning
@SuppressWarnings("squid:S3516")
@Override @Override
public int onStartCommand(final Intent intent, final int flags, final int startId) { public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (DEBUG) { if (DEBUG) {
@ -127,8 +133,8 @@ public final class PlayerService extends MediaBrowserServiceCompat {
return START_NOT_STICKY; return START_NOT_STICKY;
} }
initializePlayer(); initializePlayerIfNeeded();
player.handleIntent(intent); Objects.requireNonNull(player).handleIntent(intent);
player.UIs().get(MediaSessionPlayerUi.class) player.UIs().get(MediaSessionPlayerUi.class)
.ifPresent(ui -> ui.handleMediaButtonIntent(intent)); .ifPresent(ui -> ui.handleMediaButtonIntent(intent));
@ -164,11 +170,14 @@ public final class PlayerService extends MediaBrowserServiceCompat {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "destroy() called"); Log.d(TAG, "destroy() called");
} }
cleanup(); cleanup();
if (mediaBrowserConnector != null) { if (mediaBrowserConnector != null) {
mediaBrowserConnector.release(); mediaBrowserConnector.release();
mediaBrowserConnector = null; mediaBrowserConnector = null;
} }
compositeDisposableLoadChildren.clear(); compositeDisposableLoadChildren.clear();
} }
@ -190,36 +199,23 @@ public final class PlayerService extends MediaBrowserServiceCompat {
} }
@Override @Override
public IBinder onBind(final Intent intent) { public IBinder onBind(@NonNull final Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) { if (SERVICE_INTERFACE.equals(intent.getAction())) {
return super.onBind(intent); return super.onBind(intent);
} }
return mBinder; return mBinder;
} }
@NonNull
public MediaSessionConnector getSessionConnector() { public MediaSessionConnector getSessionConnector() {
return mediaBrowserConnector.getSessionConnector(); return mediaBrowserConnector.getSessionConnector();
} }
public static class LocalBinder extends Binder {
private final WeakReference<PlayerService> playerService;
LocalBinder(final PlayerService playerService) {
this.playerService = new WeakReference<>(playerService);
}
public PlayerService getService() {
return playerService.get();
}
public Player getPlayer() {
return playerService.get().player;
}
}
// MediaBrowserServiceCompat methods // MediaBrowserServiceCompat methods
@Nullable @Nullable
@Override @Override
public BrowserRoot onGetRoot(@NonNull final String clientPackageName, final int clientUid, public BrowserRoot onGetRoot(@NonNull final String clientPackageName,
final int clientUid,
@Nullable final Bundle rootHints) { @Nullable final Bundle rootHints) {
return mediaBrowserConnector.onGetRoot(clientPackageName, clientUid, rootHints); return mediaBrowserConnector.onGetRoot(clientPackageName, clientUid, rootHints);
} }
@ -228,8 +224,26 @@ public final class PlayerService extends MediaBrowserServiceCompat {
public void onLoadChildren(@NonNull final String parentId, public void onLoadChildren(@NonNull final String parentId,
@NonNull final Result<List<MediaItem>> result) { @NonNull final Result<List<MediaItem>> result) {
result.detach(); result.detach();
final var disposable = mediaBrowserConnector.onLoadChildren(parentId) final Disposable disposable = mediaBrowserConnector.onLoadChildren(parentId)
.subscribe(result::sendResult); .subscribe(result::sendResult);
compositeDisposableLoadChildren.add(disposable); compositeDisposableLoadChildren.add(disposable);
} }
public static final class LocalBinder extends Binder {
private final WeakReference<PlayerService> playerService;
LocalBinder(final PlayerService playerService) {
this.playerService = new WeakReference<>(playerService);
}
@Nullable
public PlayerService getService() {
return playerService.get();
}
@Nullable
public Player getPlayer() {
return playerService.get().player;
}
}
} }

View File

@ -23,12 +23,14 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.AppDatabase; import org.schabi.newpipe.database.AppDatabase;
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -44,11 +46,15 @@ import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.Disposable;
public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPreparer { public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPreparer {
private static final String TAG = MediaBrowserConnector.class.getSimpleName(); private static final String TAG = MediaBrowserConnector.class.getSimpleName();
@NonNull
private final PlayerService playerService; private final PlayerService playerService;
private final @NonNull MediaSessionConnector sessionConnector; @NonNull
private final @NonNull MediaSessionCompat mediaSession; private final MediaSessionConnector sessionConnector;
@NonNull
private final MediaSessionCompat mediaSession;
private AppDatabase database; private AppDatabase database;
private LocalPlaylistManager localPlaylistManager; private LocalPlaylistManager localPlaylistManager;
@ -63,7 +69,8 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
playerService.setSessionToken(mediaSession.getSessionToken()); playerService.setSessionToken(mediaSession.getSessionToken());
} }
public @NonNull MediaSessionConnector getSessionConnector() { @NonNull
public MediaSessionConnector getSessionConnector() {
return sessionConnector; return sessionConnector;
} }
@ -86,32 +93,35 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
builder.setMediaId(mediaId); builder.setMediaId(mediaId);
builder.setTitle(folderName); builder.setTitle(folderName);
final var extras = new Bundle(); final Bundle extras = new Bundle();
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
playerService.getString(R.string.app_name)); playerService.getString(R.string.app_name));
builder.setExtras(extras); builder.setExtras(extras);
return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE); return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE);
} }
private MediaItem createPlaylistMediaItem(final PlaylistMetadataEntry playlist) { @NonNull
private MediaItem createPlaylistMediaItem(@NonNull final PlaylistMetadataEntry playlist) {
final var builder = new MediaDescriptionCompat.Builder(); final var builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(createMediaIdForPlaylist(playlist.getUid())) builder.setMediaId(createMediaIdForPlaylist(playlist.getUid()))
.setTitle(playlist.name) .setTitle(playlist.name)
.setIconUri(Uri.parse(playlist.thumbnailUrl)); .setIconUri(Uri.parse(playlist.thumbnailUrl));
final var extras = new Bundle(); final Bundle extras = new Bundle();
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
playerService.getResources().getString(R.string.tab_bookmarks)); playerService.getResources().getString(R.string.tab_bookmarks));
builder.setExtras(extras); builder.setExtras(extras);
return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE); return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE);
} }
@NonNull
private String createMediaIdForPlaylist(final long playlistId) { private String createMediaIdForPlaylist(final long playlistId) {
return ID_BOOKMARKS + '/' + playlistId; return ID_BOOKMARKS + '/' + playlistId;
} }
@NonNull
private MediaItem createPlaylistStreamMediaItem(final long playlistId, private MediaItem createPlaylistStreamMediaItem(final long playlistId,
final PlaylistStreamEntry item, @NonNull final PlaylistStreamEntry item,
final int index) { final int index) {
final var builder = new MediaDescriptionCompat.Builder(); final var builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(createMediaIdForPlaylistIndex(playlistId, index)) builder.setMediaId(createMediaIdForPlaylistIndex(playlistId, index))
@ -121,6 +131,7 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE); return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE);
} }
@NonNull
private String createMediaIdForPlaylistIndex(final long playlistId, final int index) { private String createMediaIdForPlaylistIndex(final long playlistId, final int index) {
return createMediaIdForPlaylist(playlistId) + '/' + index; return createMediaIdForPlaylist(playlistId) + '/' + index;
} }
@ -143,7 +154,6 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
} }
final List<MediaItem> mediaItems = new ArrayList<>(); final List<MediaItem> mediaItems = new ArrayList<>();
final var parentIdUri = Uri.parse(parentId);
if (parentId.equals(ID_ROOT)) { if (parentId.equals(ID_ROOT)) {
mediaItems.add( mediaItems.add(
@ -153,14 +163,15 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
createRootMediaItem(ID_HISTORY, createRootMediaItem(ID_HISTORY,
playerService.getResources().getString(R.string.action_history))); playerService.getResources().getString(R.string.action_history)));
} else if (parentId.startsWith(ID_BOOKMARKS)) { } else if (parentId.startsWith(ID_BOOKMARKS)) {
final var path = parentIdUri.getPathSegments(); final Uri parentIdUri = Uri.parse(parentId);
final List<String> path = parentIdUri.getPathSegments();
if (path.size() == 2) { if (path.size() == 2) {
return populateBookmarks(); return populateBookmarks();
} else if (path.size() == 3) { } else if (path.size() == 3) {
final var playlistId = Long.parseLong(path.get(2)); final long playlistId = Long.parseLong(path.get(2));
return populatePlaylist(playlistId); return populatePlaylist(playlistId);
} else { } else {
Log.w(TAG, "Unknown playlist uri " + parentId); Log.w(TAG, "Unknown playlist URI: " + parentId);
} }
} else if (parentId.equals(ID_HISTORY)) { } else if (parentId.equals(ID_HISTORY)) {
return populateHistory(); return populateHistory();
@ -169,17 +180,19 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
} }
private Single<List<MediaItem>> populateHistory() { private Single<List<MediaItem>> populateHistory() {
final var streamHistory = getDatabase().streamHistoryDAO(); final StreamHistoryDAO streamHistory = getDatabase().streamHistoryDAO();
final var history = streamHistory.getHistory().firstOrError(); final var history = streamHistory.getHistory().firstOrError();
return history.map(items -> return history.map(items -> items.stream()
items.stream().map(this::createHistoryMediaItem).collect(Collectors.toList())); .map(this::createHistoryMediaItem)
.collect(Collectors.toList()));
} }
@NonNull
private MediaItem createHistoryMediaItem(@NonNull final StreamHistoryEntry streamHistoryEntry) { private MediaItem createHistoryMediaItem(@NonNull final StreamHistoryEntry streamHistoryEntry) {
final var builder = new MediaDescriptionCompat.Builder(); final var builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(ID_STREAM + '/' + streamHistoryEntry.getStreamId()) builder.setMediaId(ID_STREAM + '/' + streamHistoryEntry.getStreamId())
.setTitle(streamHistoryEntry.getStreamEntity().getTitle()) .setTitle(streamHistoryEntry.getStreamEntity().getTitle())
.setIconUri(Uri.parse(streamHistoryEntry.getStreamEntity().getThumbnailUrl())); .setIconUri(Uri.parse(streamHistoryEntry.getStreamEntity().getThumbnailUrl()));
return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE); return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE);
} }
@ -198,10 +211,14 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
return localPlaylistManager; return localPlaylistManager;
} }
// Suppress Sonar warning replace list collection by Stream.toList call, as this method is only
// available in Android API 34 and not currently available with desugaring
@SuppressWarnings("squid:S6204")
private Single<List<MediaItem>> populateBookmarks() { private Single<List<MediaItem>> populateBookmarks() {
final var playlists = getPlaylistManager().getPlaylists().firstOrError(); final var playlists = getPlaylistManager().getPlaylists().firstOrError();
return playlists.map(playlist -> return playlists.map(playlist -> playlist.stream()
playlist.stream().map(this::createPlaylistMediaItem).collect(Collectors.toList())); .map(this::createPlaylistMediaItem)
.collect(Collectors.toList()));
} }
private Single<List<MediaItem>> populatePlaylist(final long playlistId) { private Single<List<MediaItem>> populatePlaylist(final long playlistId) {
@ -209,7 +226,7 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
return playlist.map(items -> { return playlist.map(items -> {
final List<MediaItem> results = new ArrayList<>(); final List<MediaItem> results = new ArrayList<>();
int index = 0; int index = 0;
for (final var item : items) { for (final PlaylistStreamEntry item : items) {
results.add(createPlaylistStreamMediaItem(playlistId, item, index)); results.add(createPlaylistStreamMediaItem(playlistId, item, index));
++index; ++index;
} }
@ -231,36 +248,33 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
if (mediaIdUri == null) { if (mediaIdUri == null) {
return Single.error(new ContentNotAvailableException("Media ID cannot be parsed")); return Single.error(new ContentNotAvailableException("Media ID cannot be parsed"));
} }
if (mediaId.startsWith(ID_BOOKMARKS)) {
final var path = mediaIdUri.getPathSegments();
if (path.size() == 4) {
final long playlistId = Long.parseLong(path.get(2));
final int index = Integer.parseInt(path.get(3));
return getPlaylistManager() final List<String> path = mediaIdUri.getPathSegments();
.getPlaylistStreams(playlistId)
.firstOrError() if (mediaId.startsWith(ID_BOOKMARKS) && path.size() == 4) {
.map(items -> { final long playlistId = Long.parseLong(path.get(2));
final var infoItems = items.stream() final int index = Integer.parseInt(path.get(3));
.map(PlaylistStreamEntry::toStreamInfoItem)
.collect(Collectors.toList()); return getPlaylistManager()
return new SinglePlayQueue(infoItems, index); .getPlaylistStreams(playlistId)
}); .firstOrError()
} .map(items -> {
} else if (mediaId.startsWith(ID_STREAM)) { final List<StreamInfoItem> infoItems = items.stream()
final var path = mediaIdUri.getPathSegments(); .map(PlaylistStreamEntry::toStreamInfoItem)
if (path.size() == 3) { .collect(Collectors.toList());
final long streamId = Long.parseLong(path.get(2)); return new SinglePlayQueue(infoItems, index);
return getDatabase().streamHistoryDAO().getHistory() });
.firstOrError() } else if (mediaId.startsWith(ID_STREAM) && path.size() == 3) {
.map(items -> { final long streamId = Long.parseLong(path.get(2));
final var infoItems = items.stream() return getDatabase().streamHistoryDAO().getHistory()
.filter(it -> it.getStreamId() == streamId) .firstOrError()
.map(StreamHistoryEntry::toStreamInfoItem) .map(items -> {
.collect(Collectors.toList()); final List<StreamInfoItem> infoItems = items.stream()
return new SinglePlayQueue(infoItems, 0); .filter(it -> it.getStreamId() == streamId)
}); .map(StreamHistoryEntry::toStreamInfoItem)
} .collect(Collectors.toList());
return new SinglePlayQueue(infoItems, 0);
});
} }
return Single.error(new ContentNotAvailableException("Media ID cannot be parsed")); return Single.error(new ContentNotAvailableException("Media ID cannot be parsed"));
@ -285,7 +299,8 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
} }
@Override @Override
public void onPrepareFromMediaId(@NonNull final String mediaId, final boolean playWhenReady, public void onPrepareFromMediaId(@NonNull final String mediaId,
final boolean playWhenReady,
@Nullable final Bundle extras) { @Nullable final Bundle extras) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, String.format("MediaBrowserConnector.onPrepareFromMediaId(%s, %s, %s)", Log.d(TAG, String.format("MediaBrowserConnector.onPrepareFromMediaId(%s, %s, %s)",
@ -307,22 +322,26 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
} }
@Override @Override
public void onPrepareFromSearch(@NonNull final String query, final boolean playWhenReady, public void onPrepareFromSearch(@NonNull final String query,
final boolean playWhenReady,
@Nullable final Bundle extras) { @Nullable final Bundle extras) {
disposePrepareOrPlayCommands(); disposePrepareOrPlayCommands();
playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED); playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED);
} }
@Override @Override
public void onPrepareFromUri(@NonNull final Uri uri, final boolean playWhenReady, public void onPrepareFromUri(@NonNull final Uri uri,
final boolean playWhenReady,
@Nullable final Bundle extras) { @Nullable final Bundle extras) {
disposePrepareOrPlayCommands(); disposePrepareOrPlayCommands();
playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED); playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED);
} }
@Override @Override
public boolean onCommand(@NonNull final Player player, @NonNull final String command, public boolean onCommand(@NonNull final Player player,
@Nullable final Bundle extras, @Nullable final ResultReceiver cb) { @NonNull final String command,
@Nullable final Bundle extras,
@Nullable final ResultReceiver cb) {
return false; return false;
} }
} }