From 993374e56c314e0f5eb49f43e93f0d58b75fe30d Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 Jan 2026 09:27:53 -0800 Subject: [PATCH] fix: adde a scheduled delay to allow callbacks to succeed --- .../tempo/repository/ArtistRepository.java | 13 +++ .../AlbumBottomSheetDialog.java | 55 +++++++---- .../ArtistBottomSheetDialog.java | 96 +++++++++++++------ .../viewmodel/AlbumBottomSheetViewModel.java | 4 +- 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java index 7cd69c20..cc47e676 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/ArtistRepository.java @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData; import android.util.Log; import com.cappielloantonio.tempo.App; +import com.cappielloantonio.tempo.interfaces.MediaCallback; import com.cappielloantonio.tempo.subsonic.base.ApiResponse; import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.AlbumID3; @@ -292,6 +293,18 @@ public class ArtistRepository { return new SongRepository().getInstantMix(artist.getId(), SeedType.ARTIST, count); } + public void getInstantMix(ArtistID3 artist, int count, final MediaCallback callback) { + // Delegate to the centralized SongRepository + new SongRepository().getInstantMix(artist.getId(), SeedType.ARTIST, count, songs -> { + if (songs != null && !songs.isEmpty()) { + callback.onLoadMedia(songs); + } else { + callback.onLoadMedia(Collections.emptyList()); + } + }); + } + + public MutableLiveData> getRandomSong(ArtistID3 artist, int count) { MutableLiveData> randomSongs = new MutableLiveData<>(); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java index 563b1ab3..06db820b 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/AlbumBottomSheetDialog.java @@ -5,7 +5,6 @@ import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; -import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -62,6 +61,9 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements private List currentAlbumTracks = Collections.emptyList(); private List currentAlbumMediaItems = Collections.emptyList(); + private boolean playbackStarted = false; + private boolean dismissalScheduled = false; + private ListenableFuture mediaBrowserListenableFuture; private static final String TAG = "AlbumBottomSheetDialog"; @@ -120,11 +122,16 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements TextView playRadio = view.findViewById(R.id.play_radio_text_view); playRadio.setOnClickListener(v -> { + playbackStarted = false; + dismissalScheduled = false; Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show(); new AlbumRepository().getInstantMix(album, 20, new MediaCallback() { @Override public void onError(Exception exception) { Log.e(TAG, "Error: " + exception.getMessage()); + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } } @Override @@ -136,30 +143,26 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements MusicUtil.ratingFilter((ArrayList) media); if (!media.isEmpty()) { + boolean isFirstBatch = !playbackStarted; MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList) media, 0); + playbackStarted = true; + if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setBottomSheetInPeek(true); } - } - - view.postDelayed(() -> { - try { - if (mediaBrowserListenableFuture.isDone()) { - MediaBrowser browser = mediaBrowserListenableFuture.get(); - if (browser != null && browser.isPlaying()) { - dismissBottomSheet(); - return; - } - } - } catch (Exception e) { - Log.e(TAG, "Error checking playback: " + e.getMessage()); + if (isFirstBatch && !dismissalScheduled) { + scheduleDelayedDismissal(v); } - view.postDelayed(() -> dismissBottomSheet(), 300); - }, 300); + } else { + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } } }); }); + TextView playRandom = view.findViewById(R.id.play_random_text_view); playRandom.setOnClickListener(v -> { AlbumRepository albumRepository = new AlbumRepository(); @@ -308,4 +311,24 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements private void refreshShares() { homeViewModel.refreshShares(requireActivity()); } + + private void scheduleDelayedDismissal(View view) { + if (dismissalScheduled) return; + dismissalScheduled = true; + + view.postDelayed(() -> { + try { + if (mediaBrowserListenableFuture.isDone()) { + MediaBrowser browser = mediaBrowserListenableFuture.get(); + if (browser != null && browser.isPlaying()) { + dismissBottomSheet(); + return; + } + } + } catch (Exception e) { + Log.e(TAG, "Error checking playback: " + e.getMessage()); + } + view.postDelayed(() -> dismissBottomSheet(), 200); + }, 300); + } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/ArtistBottomSheetDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/ArtistBottomSheetDialog.java index 4d50565a..91b2cf90 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/ArtistBottomSheetDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/ArtistBottomSheetDialog.java @@ -19,6 +19,7 @@ import androidx.media3.session.SessionToken; import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.glide.CustomGlideRequest; +import com.cappielloantonio.tempo.interfaces.MediaCallback; import com.cappielloantonio.tempo.repository.ArtistRepository; import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaService; @@ -31,6 +32,7 @@ import com.cappielloantonio.tempo.viewmodel.ArtistBottomSheetViewModel; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; import java.util.List; @UnstableApi @@ -42,6 +44,9 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement private ListenableFuture mediaBrowserListenableFuture; + private boolean playbackStarted = false; + private boolean dismissalScheduled = false; + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -91,36 +96,48 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement TextView playRadio = view.findViewById(R.id.play_radio_text_view); playRadio.setOnClickListener(v -> { Log.d(TAG, "Artist instant mix clicked"); - Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_LONG).show(); - ArtistRepository artistRepository = new ArtistRepository(); - artistRepository.getInstantMix(artist, 20) - .observe(getViewLifecycleOwner(), new androidx.lifecycle.Observer>() { - @Override - public void onChanged(List songs) { - if (songs != null && !songs.isEmpty()) { - Log.d(TAG, "Starting queue with " + songs.size() + " songs"); - MusicUtil.ratingFilter(songs); - MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0); - ((MainActivity) requireActivity()).setBottomSheetInPeek(true); - artistRepository.getInstantMix(artist, 20) - .removeObserver(this); - view.postDelayed(() -> { - try { - if (mediaBrowserListenableFuture.isDone()) { - MediaBrowser browser = mediaBrowserListenableFuture.get(); - if (browser != null && browser.isPlaying()) { - dismissBottomSheet(); - return; - } - } - } catch (Exception e) { - // Ignore - } - view.postDelayed(() -> dismissBottomSheet(), 300); - }, 300); + Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show(); + playbackStarted = false; + dismissalScheduled = false; + + new ArtistRepository().getInstantMix(artist, 20, new MediaCallback() { + @Override + public void onError(Exception exception) { + Log.e(TAG, "Error: " + exception.getMessage()); + + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } + + @Override + public void onLoadMedia(List media) { + if (!isAdded() || getActivity() == null) { + return; + } + + Log.d(TAG, "Received " + media.size() + " songs for artist"); + + MusicUtil.ratingFilter((ArrayList) media); + + if (!media.isEmpty()) { + boolean isFirstBatch = !playbackStarted; + MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList) media, 0); + playbackStarted = true; + + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).setBottomSheetInPeek(true); + } + if (isFirstBatch && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } else { + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); } } - }); + } + }); }); TextView playRandom = view.findViewById(R.id.play_random_text_view); @@ -153,4 +170,25 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement private void releaseMediaBrowser() { MediaBrowser.releaseFuture(mediaBrowserListenableFuture); } -} \ No newline at end of file + + private void scheduleDelayedDismissal(View view) { + if (dismissalScheduled) return; + dismissalScheduled = true; + + view.postDelayed(() -> { + try { + if (mediaBrowserListenableFuture.isDone()) { + MediaBrowser browser = mediaBrowserListenableFuture.get(); + if (browser != null && browser.isPlaying()) { + dismissBottomSheet(); + return; + } + } + } catch (Exception e) { + Log.e(TAG, "Error checking playback: " + e.getMessage()); + } + view.postDelayed(() -> dismissBottomSheet(), 200); + }, 300); + } + +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/AlbumBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/AlbumBottomSheetViewModel.java index da1ec831..07b17a48 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/AlbumBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/AlbumBottomSheetViewModel.java @@ -4,10 +4,12 @@ import android.app.Application; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.OptIn; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; +import androidx.media3.common.util.UnstableApi; import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.interfaces.StarCallback; @@ -33,7 +35,6 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel { private final ArtistRepository artistRepository; private final FavoriteRepository favoriteRepository; private final SharingRepository sharingRepository; - private AlbumID3 album; public AlbumBottomSheetViewModel(@NonNull Application application) { @@ -116,6 +117,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel { MutableLiveData> tracksLiveData = albumRepository.getAlbumTracks(album.getId()); tracksLiveData.observeForever(new Observer>() { + @OptIn(markerClass = UnstableApi.class) @Override public void onChanged(List songs) { if (songs != null && !songs.isEmpty()) {