From 431014adc4b4898864e8d0e71f6c4bba64c40a37 Mon Sep 17 00:00:00 2001 From: eddyizm Date: Sun, 4 Jan 2026 11:31:53 -0800 Subject: [PATCH] fix: updated song bottom sheet to match album/artist bottom sheets --- .../SongBottomSheetDialog.java | 115 ++++++++++++++---- .../viewmodel/SongBottomSheetViewModel.java | 12 ++ 2 files changed, 102 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java index 271f00cc..27f15b3d 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/bottomsheetdialog/SongBottomSheetDialog.java @@ -7,6 +7,7 @@ import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -25,6 +26,7 @@ import androidx.navigation.fragment.NavHostFragment; import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.glide.CustomGlideRequest; +import com.cappielloantonio.tempo.interfaces.MediaCallback; import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaService; @@ -52,6 +54,7 @@ import com.cappielloantonio.tempo.util.ExternalAudioWriter; import java.util.ArrayList; import java.util.Collections; +import java.util.List; @UnstableApi public class SongBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener { @@ -69,7 +72,11 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements private AssetLinkUtil.AssetLink currentAlbumLink; private AssetLinkUtil.AssetLink currentArtistLink; + private boolean playbackStarted = false; + private boolean dismissalScheduled = false; + private ListenableFuture mediaBrowserListenableFuture; + private static final String TAG = "SongBottomSheetDialog"; @Nullable @Override @@ -145,37 +152,68 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements TextView playRadio = view.findViewById(R.id.play_radio_text_view); playRadio.setOnClickListener(v -> { - ((MainActivity) requireActivity()).setBottomSheetInPeek(true); + playbackStarted = false; + dismissalScheduled = false; + Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show(); - final boolean[] playbackStarted = {false}; + final Runnable failsafeTimeout = () -> { + if (!playbackStarted && !dismissalScheduled) { + Log.w(TAG, "No response received within 3 seconds"); + if (isAdded() && getActivity() != null) { + Toast.makeText(getContext(), + R.string.bottom_sheet_problem_generating_instant_mix, + Toast.LENGTH_SHORT).show(); + dismissBottomSheet(); + } + } + }; + view.postDelayed(failsafeTimeout, 3000); + songBottomSheetViewModel.getInstantMix(song, 20, new MediaCallback() { + @Override + public void onError(Exception exception) { + view.removeCallbacks(failsafeTimeout); + Log.e(TAG, "Error: " + exception.getMessage()); + if (isAdded() && getActivity() != null) { + String message = isOffline(exception) ? + "You're offline" : "Network error"; + Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); + } + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } - songBottomSheetViewModel.getInstantMix(getViewLifecycleOwner(), song).observe(getViewLifecycleOwner(), songs -> { - if (playbackStarted[0] || songs == null || songs.isEmpty()) return; + @Override + public void onLoadMedia(List media) { + view.removeCallbacks(failsafeTimeout); + if (!isAdded() || getActivity() == null) { + return; + } - new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (playbackStarted[0]) return; + MusicUtil.ratingFilter((ArrayList) media); - MusicUtil.ratingFilter(songs); - - MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0); - playbackStarted[0] = true; - - view.postDelayed(() -> { - try { - if (mediaBrowserListenableFuture.isDone()) { - MediaBrowser browser = mediaBrowserListenableFuture.get(); - if (browser != null && browser.isPlaying()) { - dismissBottomSheet(); - return; - } - } - } catch (Exception e) { - // Ignore + if (!media.isEmpty()) { + boolean isFirstBatch = !playbackStarted; + MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList) media, 0); + playbackStarted = true; + + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).setBottomSheetInPeek(true); } - view.postDelayed(() -> dismissBottomSheet(), 200); - }, 300); - }, 300); + if (isFirstBatch && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } else { + Toast.makeText(getContext(), + R.string.bottom_sheet_problem_generating_instant_mix, + Toast.LENGTH_SHORT).show(); + if (!playbackStarted && !dismissalScheduled) { + scheduleDelayedDismissal(v); + } + } + } }); + }); TextView playNext = view.findViewById(R.id.play_next_text_view); @@ -414,4 +452,31 @@ public class SongBottomSheetDialog 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); + } + + private boolean isOffline(Exception exception) { + return exception != null && exception.getMessage() != null && + (exception.getMessage().contains("Network") || + exception.getMessage().contains("timeout") || + exception.getMessage().contains("offline")); + } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java index efa74e90..665756a7 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/SongBottomSheetViewModel.java @@ -10,6 +10,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.media3.common.util.UnstableApi; +import com.cappielloantonio.tempo.interfaces.MediaCallback; import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.repository.AlbumRepository; @@ -134,6 +135,17 @@ public class SongBottomSheetViewModel extends AndroidViewModel { return instantMix; } + public void getInstantMix(Child media, int count, MediaCallback callback) { + + songRepository.getInstantMix(media.getId(), SeedType.TRACK, count, songs -> { + if (songs != null && !songs.isEmpty()) { + callback.onLoadMedia(songs); + } else { + callback.onLoadMedia(Collections.emptyList()); + } + }); + } + public MutableLiveData shareTrack() { return sharingRepository.createShare(song.getId(), song.getTitle(), null); }