feat: Mark currently playing song in SongHorizontalAdapter

This commit is contained in:
Jaime García 2025-09-22 00:15:52 +02:00
parent c62d2ace4d
commit 52ba783a90
No known key found for this signature in database
GPG key ID: BC4E5F71A71BDA5B
10 changed files with 162 additions and 0 deletions

View file

@ -0,0 +1,8 @@
package com.cappielloantonio.tempo.interfaces;
import androidx.annotation.Keep;
@Keep
public interface MediaSongIdCallback {
default void onRecovery(String id) {}
}

View file

@ -12,6 +12,7 @@ import androidx.media3.session.SessionToken;
import com.cappielloantonio.tempo.App;
import com.cappielloantonio.tempo.interfaces.MediaIndexCallback;
import com.cappielloantonio.tempo.interfaces.MediaSongIdCallback;
import com.cappielloantonio.tempo.model.Chronology;
import com.cappielloantonio.tempo.repository.ChronologyRepository;
import com.cappielloantonio.tempo.repository.QueueRepository;
@ -292,6 +293,25 @@ public class MediaManager {
}
}
public static void getCurrentSongId(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, MediaSongIdCallback callback) {
if (mediaBrowserListenableFuture != null) {
mediaBrowserListenableFuture.addListener(() -> {
try {
if (mediaBrowserListenableFuture.isDone()) {
MediaItem currentItem = mediaBrowserListenableFuture.get().getCurrentMediaItem();
if (currentItem != null) {
callback.onRecovery(currentItem.mediaMetadata.extras.getString("id"));
} else {
callback.onRecovery(null);
}
}
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
}
}
public static void setLastPlayedTimestamp(MediaItem mediaItem) {
if (mediaItem != null) getQueueRepository().setLastPlayedTimestamp(mediaItem.mediaId);
}

View file

@ -10,12 +10,15 @@ import android.widget.Filterable;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaBrowser;
import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.databinding.ItemHorizontalTrackBinding;
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.interfaces.MediaSongIdCallback;
import com.cappielloantonio.tempo.service.MediaManager;
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.DiscTitle;
@ -23,6 +26,7 @@ import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.MusicUtil;
import com.cappielloantonio.tempo.util.Preferences;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.Collections;
@ -41,6 +45,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
private List<Child> songsFull;
private List<Child> songs;
private String currentFilter;
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
private final Filter filtering = new Filter() {
@Override
@ -165,6 +170,21 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
} else {
holder.item.ratingIndicatorImageView.setVisibility(View.GONE);
}
MediaManager.getCurrentSongId(mediaBrowserListenableFuture, new MediaSongIdCallback() {
@Override
public void onRecovery(String id) {
if (song.getId().equals(id)) {
holder.item.playPauseIcon.setVisibility(View.VISIBLE);
if (!showCoverArt) holder.item.trackNumberTextView.setVisibility(View.INVISIBLE);
if (showCoverArt) holder.item.coverArtOverlay.setVisibility(View.VISIBLE);
} else {
holder.item.playPauseIcon.setVisibility(View.INVISIBLE);
if (showCoverArt) holder.item.coverArtOverlay.setVisibility(View.INVISIBLE);
if (!showCoverArt) holder.item.trackNumberTextView.setVisibility(View.VISIBLE);
}
}
});
}
@Override
@ -247,4 +267,8 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
notifyDataSetChanged();
}
public void setMediaBrowserListenableFuture(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture) {
this.mediaBrowserListenableFuture = mediaBrowserListenableFuture;
}
}

View file

@ -93,6 +93,12 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
initializeMediaBrowser();
}
@Override
public void onResume() {
super.onResume();
setMediaBrowserListenableFuture();
}
@Override
public void onStop() {
releaseMediaBrowser();
@ -271,6 +277,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
songHorizontalAdapter = new SongHorizontalAdapter(this, false, false, album);
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
setMediaBrowserListenableFuture();
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
}
@ -288,6 +295,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
@Override
public void onMediaClick(Bundle bundle) {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
songHorizontalAdapter.notifyDataSetChanged();
activity.setBottomSheetInPeek(true);
}
@ -295,4 +303,10 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
public void onMediaLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.songBottomSheetDialog, bundle);
}
private void setMediaBrowserListenableFuture() {
if (songHorizontalAdapter != null) {
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -82,6 +82,12 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
initializeMediaBrowser();
}
@Override
public void onResume() {
super.onResume();
setMediaBrowserListenableFuture();
}
@Override
public void onStop() {
releaseMediaBrowser();
@ -174,6 +180,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
songHorizontalAdapter = new SongHorizontalAdapter(this, true, true, null);
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
setMediaBrowserListenableFuture();
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
if (songs == null) {
if (bind != null) bind.artistPageTopSongsSector.setVisibility(View.GONE);
@ -246,6 +253,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
@Override
public void onMediaClick(Bundle bundle) {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
songHorizontalAdapter.notifyDataSetChanged();
activity.setBottomSheetInPeek(true);
}
@ -273,4 +281,10 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
public void onArtistLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.artistBottomSheetDialog, bundle);
}
private void setMediaBrowserListenableFuture() {
if (songHorizontalAdapter != null) {
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -144,6 +144,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
public void onResume() {
super.onResume();
refreshSharesView();
setTopSongMediaBrowserListenableFuture();
setStarredSongMediaBrowserListenableFuture();
}
@Override
@ -477,6 +479,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
topSongAdapter = new SongHorizontalAdapter(this, true, false, null);
bind.topSongsRecyclerView.setAdapter(topSongAdapter);
setTopSongMediaBrowserListenableFuture();
homeViewModel.getChronologySample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), chronologies -> {
if (chronologies == null || chronologies.isEmpty()) {
if (bind != null) bind.homeGridTracksSector.setVisibility(View.GONE);
@ -515,6 +518,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
starredSongAdapter = new SongHorizontalAdapter(this, true, false, null);
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
setStarredSongMediaBrowserListenableFuture();
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
if (songs == null) {
if (bind != null) bind.starredTracksSector.setVisibility(View.GONE);
@ -954,6 +958,8 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
activity.setBottomSheetInPeek(true);
}
topSongAdapter.notifyDataSetChanged();
starredSongAdapter.notifyDataSetChanged();
}
@Override
@ -1043,4 +1049,16 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
public void onShareLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.shareBottomSheetDialog, bundle);
}
private void setTopSongMediaBrowserListenableFuture() {
if (topSongAdapter != null) {
topSongAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
private void setStarredSongMediaBrowserListenableFuture() {
if (starredSongAdapter != null) {
starredSongAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -111,6 +111,12 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
initializeMediaBrowser();
}
@Override
public void onResume() {
super.onResume();
setMediaBrowserListenableFuture();
}
@Override
public void onStop() {
releaseMediaBrowser();
@ -248,6 +254,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false, null);
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
setMediaBrowserListenableFuture();
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
}
@ -263,6 +270,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
@Override
public void onMediaClick(Bundle bundle) {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
songHorizontalAdapter.notifyDataSetChanged();
activity.setBottomSheetInPeek(true);
}
@ -270,4 +278,10 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
public void onMediaLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.songBottomSheetDialog, bundle);
}
private void setMediaBrowserListenableFuture() {
if (songHorizontalAdapter != null) {
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -75,6 +75,12 @@ public class SearchFragment extends Fragment implements ClickCallback {
initializeMediaBrowser();
}
@Override
public void onResume() {
super.onResume();
setMediaBrowserListenableFuture();
}
@Override
public void onStop() {
releaseMediaBrowser();
@ -113,6 +119,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false, null);
setMediaBrowserListenableFuture();
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
}
@ -260,6 +267,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
@Override
public void onMediaClick(Bundle bundle) {
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
songHorizontalAdapter.notifyDataSetChanged();
activity.setBottomSheetInPeek(true);
}
@ -287,4 +295,10 @@ public class SearchFragment extends Fragment implements ClickCallback {
public void onArtistLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.artistBottomSheetDialog, bundle);
}
private void setMediaBrowserListenableFuture() {
if (songHorizontalAdapter != null) {
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -84,6 +84,12 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
initializeMediaBrowser();
}
@Override
public void onResume() {
super.onResume();
setMediaBrowserListenableFuture();
}
@Override
public void onStop() {
releaseMediaBrowser();
@ -191,6 +197,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false, null);
bind.songListRecyclerView.setAdapter(songHorizontalAdapter);
setMediaBrowserListenableFuture();
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
isLoading = false;
songHorizontalAdapter.setItems(songs);
@ -318,6 +325,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
public void onMediaClick(Bundle bundle) {
hideKeyboard(requireView());
MediaManager.startQueue(mediaBrowserListenableFuture, bundle.getParcelableArrayList(Constants.TRACKS_OBJECT), bundle.getInt(Constants.ITEM_POSITION));
songHorizontalAdapter.notifyDataSetChanged();
activity.setBottomSheetInPeek(true);
}
@ -325,4 +333,10 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
public void onMediaLongClick(Bundle bundle) {
Navigation.findNavController(requireView()).navigate(R.id.songBottomSheetDialog, bundle);
}
private void setMediaBrowserListenableFuture() {
if (songHorizontalAdapter != null) {
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
}
}
}

View file

@ -55,6 +55,28 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/different_disk_divider_sector" />
<View
android:id="@+id/cover_art_overlay"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginStart="16dp"
android:background="#80000000"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/song_cover_image_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/different_disk_divider_sector" />
<ImageView
android:id="@+id/play_pause_icon"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginStart="28dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/different_disk_divider_sector"
android:src="@drawable/button_play_pause_selector" />
<TextView
android:id="@+id/track_number_text_view"
style="@style/LabelLarge"