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"
android:resource="@xml/automotive_app_desc" />
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@mipmap/ic_launcher" />
android:resource="@mipmap/ic_launcher" />
</application>
</manifest>

View File

@ -31,7 +31,12 @@ data class StreamHistoryEntry(
}
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.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl

View File

@ -43,8 +43,10 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
/**
* 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 boolean DEBUG = Player.DEBUG;
@Nullable
private Player player;
private final IBinder mBinder = new PlayerService.LocalBinder(this);
@ -79,7 +82,7 @@ public final class PlayerService extends MediaBrowserServiceCompat {
mediaBrowserConnector = new MediaBrowserConnector(this);
}
private void initializePlayer() {
private void initializePlayerIfNeeded() {
if (player == null) {
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
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (DEBUG) {
@ -127,8 +133,8 @@ public final class PlayerService extends MediaBrowserServiceCompat {
return START_NOT_STICKY;
}
initializePlayer();
player.handleIntent(intent);
initializePlayerIfNeeded();
Objects.requireNonNull(player).handleIntent(intent);
player.UIs().get(MediaSessionPlayerUi.class)
.ifPresent(ui -> ui.handleMediaButtonIntent(intent));
@ -164,11 +170,14 @@ public final class PlayerService extends MediaBrowserServiceCompat {
if (DEBUG) {
Log.d(TAG, "destroy() called");
}
cleanup();
if (mediaBrowserConnector != null) {
mediaBrowserConnector.release();
mediaBrowserConnector = null;
}
compositeDisposableLoadChildren.clear();
}
@ -190,36 +199,23 @@ public final class PlayerService extends MediaBrowserServiceCompat {
}
@Override
public IBinder onBind(final Intent intent) {
public IBinder onBind(@NonNull final Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return super.onBind(intent);
}
return mBinder;
}
@NonNull
public MediaSessionConnector 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
@Nullable
@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) {
return mediaBrowserConnector.onGetRoot(clientPackageName, clientUid, rootHints);
}
@ -228,8 +224,26 @@ public final class PlayerService extends MediaBrowserServiceCompat {
public void onLoadChildren(@NonNull final String parentId,
@NonNull final Result<List<MediaItem>> result) {
result.detach();
final var disposable = mediaBrowserConnector.onLoadChildren(parentId)
final Disposable disposable = mediaBrowserConnector.onLoadChildren(parentId)
.subscribe(result::sendResult);
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.R;
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.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
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.player.PlayerService;
import org.schabi.newpipe.player.playqueue.PlayQueue;
@ -44,11 +46,15 @@ import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable;
public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPreparer {
private static final String TAG = MediaBrowserConnector.class.getSimpleName();
@NonNull
private final PlayerService playerService;
private final @NonNull MediaSessionConnector sessionConnector;
private final @NonNull MediaSessionCompat mediaSession;
@NonNull
private final MediaSessionConnector sessionConnector;
@NonNull
private final MediaSessionCompat mediaSession;
private AppDatabase database;
private LocalPlaylistManager localPlaylistManager;
@ -63,7 +69,8 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
playerService.setSessionToken(mediaSession.getSessionToken());
}
public @NonNull MediaSessionConnector getSessionConnector() {
@NonNull
public MediaSessionConnector getSessionConnector() {
return sessionConnector;
}
@ -86,32 +93,35 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
builder.setMediaId(mediaId);
builder.setTitle(folderName);
final var extras = new Bundle();
final Bundle extras = new Bundle();
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
playerService.getString(R.string.app_name));
builder.setExtras(extras);
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();
builder.setMediaId(createMediaIdForPlaylist(playlist.getUid()))
.setTitle(playlist.name)
.setIconUri(Uri.parse(playlist.thumbnailUrl));
.setTitle(playlist.name)
.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,
playerService.getResources().getString(R.string.tab_bookmarks));
builder.setExtras(extras);
return new MediaItem(builder.build(), MediaItem.FLAG_BROWSABLE);
}
@NonNull
private String createMediaIdForPlaylist(final long playlistId) {
return ID_BOOKMARKS + '/' + playlistId;
}
@NonNull
private MediaItem createPlaylistStreamMediaItem(final long playlistId,
final PlaylistStreamEntry item,
@NonNull final PlaylistStreamEntry item,
final int index) {
final var builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(createMediaIdForPlaylistIndex(playlistId, index))
@ -121,6 +131,7 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE);
}
@NonNull
private String createMediaIdForPlaylistIndex(final long playlistId, final int index) {
return createMediaIdForPlaylist(playlistId) + '/' + index;
}
@ -143,7 +154,6 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
}
final List<MediaItem> mediaItems = new ArrayList<>();
final var parentIdUri = Uri.parse(parentId);
if (parentId.equals(ID_ROOT)) {
mediaItems.add(
@ -153,14 +163,15 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
createRootMediaItem(ID_HISTORY,
playerService.getResources().getString(R.string.action_history)));
} 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) {
return populateBookmarks();
} else if (path.size() == 3) {
final var playlistId = Long.parseLong(path.get(2));
final long playlistId = Long.parseLong(path.get(2));
return populatePlaylist(playlistId);
} else {
Log.w(TAG, "Unknown playlist uri " + parentId);
Log.w(TAG, "Unknown playlist URI: " + parentId);
}
} else if (parentId.equals(ID_HISTORY)) {
return populateHistory();
@ -169,17 +180,19 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
}
private Single<List<MediaItem>> populateHistory() {
final var streamHistory = getDatabase().streamHistoryDAO();
final StreamHistoryDAO streamHistory = getDatabase().streamHistoryDAO();
final var history = streamHistory.getHistory().firstOrError();
return history.map(items ->
items.stream().map(this::createHistoryMediaItem).collect(Collectors.toList()));
return history.map(items -> items.stream()
.map(this::createHistoryMediaItem)
.collect(Collectors.toList()));
}
@NonNull
private MediaItem createHistoryMediaItem(@NonNull final StreamHistoryEntry streamHistoryEntry) {
final var builder = new MediaDescriptionCompat.Builder();
builder.setMediaId(ID_STREAM + '/' + streamHistoryEntry.getStreamId())
.setTitle(streamHistoryEntry.getStreamEntity().getTitle())
.setIconUri(Uri.parse(streamHistoryEntry.getStreamEntity().getThumbnailUrl()));
.setTitle(streamHistoryEntry.getStreamEntity().getTitle())
.setIconUri(Uri.parse(streamHistoryEntry.getStreamEntity().getThumbnailUrl()));
return new MediaItem(builder.build(), MediaItem.FLAG_PLAYABLE);
}
@ -198,10 +211,14 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
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() {
final var playlists = getPlaylistManager().getPlaylists().firstOrError();
return playlists.map(playlist ->
playlist.stream().map(this::createPlaylistMediaItem).collect(Collectors.toList()));
return playlists.map(playlist -> playlist.stream()
.map(this::createPlaylistMediaItem)
.collect(Collectors.toList()));
}
private Single<List<MediaItem>> populatePlaylist(final long playlistId) {
@ -209,7 +226,7 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
return playlist.map(items -> {
final List<MediaItem> results = new ArrayList<>();
int index = 0;
for (final var item : items) {
for (final PlaylistStreamEntry item : items) {
results.add(createPlaylistStreamMediaItem(playlistId, item, index));
++index;
}
@ -231,36 +248,33 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
if (mediaIdUri == null) {
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()
.getPlaylistStreams(playlistId)
.firstOrError()
.map(items -> {
final var infoItems = items.stream()
.map(PlaylistStreamEntry::toStreamInfoItem)
.collect(Collectors.toList());
return new SinglePlayQueue(infoItems, index);
});
}
} else if (mediaId.startsWith(ID_STREAM)) {
final var path = mediaIdUri.getPathSegments();
if (path.size() == 3) {
final long streamId = Long.parseLong(path.get(2));
return getDatabase().streamHistoryDAO().getHistory()
.firstOrError()
.map(items -> {
final var infoItems = items.stream()
.filter(it -> it.getStreamId() == streamId)
.map(StreamHistoryEntry::toStreamInfoItem)
.collect(Collectors.toList());
return new SinglePlayQueue(infoItems, 0);
});
}
final List<String> path = mediaIdUri.getPathSegments();
if (mediaId.startsWith(ID_BOOKMARKS) && path.size() == 4) {
final long playlistId = Long.parseLong(path.get(2));
final int index = Integer.parseInt(path.get(3));
return getPlaylistManager()
.getPlaylistStreams(playlistId)
.firstOrError()
.map(items -> {
final List<StreamInfoItem> infoItems = items.stream()
.map(PlaylistStreamEntry::toStreamInfoItem)
.collect(Collectors.toList());
return new SinglePlayQueue(infoItems, index);
});
} else if (mediaId.startsWith(ID_STREAM) && path.size() == 3) {
final long streamId = Long.parseLong(path.get(2));
return getDatabase().streamHistoryDAO().getHistory()
.firstOrError()
.map(items -> {
final List<StreamInfoItem> infoItems = items.stream()
.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"));
@ -285,7 +299,8 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
}
@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) {
if (DEBUG) {
Log.d(TAG, String.format("MediaBrowserConnector.onPrepareFromMediaId(%s, %s, %s)",
@ -307,22 +322,26 @@ public class MediaBrowserConnector implements MediaSessionConnector.PlaybackPrep
}
@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) {
disposePrepareOrPlayCommands();
playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED);
}
@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) {
disposePrepareOrPlayCommands();
playbackError(R.string.content_not_supported, PlaybackStateCompat.ERROR_CODE_NOT_SUPPORTED);
}
@Override
public boolean onCommand(@NonNull final Player player, @NonNull final String command,
@Nullable final Bundle extras, @Nullable final ResultReceiver cb) {
public boolean onCommand(@NonNull final Player player,
@NonNull final String command,
@Nullable final Bundle extras,
@Nullable final ResultReceiver cb) {
return false;
}
}