fix: updated song bottom sheet to match album/artist bottom sheets

This commit is contained in:
eddyizm 2026-01-04 11:31:53 -08:00
parent 6110a9c8e7
commit 431014adc4
No known key found for this signature in database
GPG key ID: CF5F671829E8158A
2 changed files with 102 additions and 25 deletions

View file

@ -7,6 +7,7 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -25,6 +26,7 @@ import androidx.navigation.fragment.NavHostFragment;
import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.glide.CustomGlideRequest; import com.cappielloantonio.tempo.glide.CustomGlideRequest;
import com.cappielloantonio.tempo.interfaces.MediaCallback;
import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.model.Download;
import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaManager;
import com.cappielloantonio.tempo.service.MediaService; import com.cappielloantonio.tempo.service.MediaService;
@ -52,6 +54,7 @@ import com.cappielloantonio.tempo.util.ExternalAudioWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
@UnstableApi @UnstableApi
public class SongBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener { 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 currentAlbumLink;
private AssetLinkUtil.AssetLink currentArtistLink; private AssetLinkUtil.AssetLink currentArtistLink;
private boolean playbackStarted = false;
private boolean dismissalScheduled = false;
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture; private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
private static final String TAG = "SongBottomSheetDialog";
@Nullable @Nullable
@Override @Override
@ -145,37 +152,68 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
TextView playRadio = view.findViewById(R.id.play_radio_text_view); TextView playRadio = view.findViewById(R.id.play_radio_text_view);
playRadio.setOnClickListener(v -> { 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 -> { @Override
if (playbackStarted[0] || songs == null || songs.isEmpty()) return; public void onLoadMedia(List<?> media) {
view.removeCallbacks(failsafeTimeout);
if (!isAdded() || getActivity() == null) {
return;
}
new Handler(Looper.getMainLooper()).postDelayed(() -> { MusicUtil.ratingFilter((ArrayList<Child>) media);
if (playbackStarted[0]) return;
MusicUtil.ratingFilter(songs); if (!media.isEmpty()) {
boolean isFirstBatch = !playbackStarted;
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0); MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
playbackStarted[0] = true; playbackStarted = true;
view.postDelayed(() -> { if (getActivity() instanceof MainActivity) {
try { ((MainActivity) getActivity()).setBottomSheetInPeek(true);
if (mediaBrowserListenableFuture.isDone()) {
MediaBrowser browser = mediaBrowserListenableFuture.get();
if (browser != null && browser.isPlaying()) {
dismissBottomSheet();
return;
}
}
} catch (Exception e) {
// Ignore
} }
view.postDelayed(() -> dismissBottomSheet(), 200); if (isFirstBatch && !dismissalScheduled) {
}, 300); scheduleDelayedDismissal(v);
}, 300); }
} 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); TextView playNext = view.findViewById(R.id.play_next_text_view);
@ -414,4 +452,31 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
private void refreshShares() { private void refreshShares() {
homeViewModel.refreshShares(requireActivity()); 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"));
}
} }

View file

@ -10,6 +10,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import com.cappielloantonio.tempo.interfaces.MediaCallback;
import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.interfaces.StarCallback;
import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.model.Download;
import com.cappielloantonio.tempo.repository.AlbumRepository; import com.cappielloantonio.tempo.repository.AlbumRepository;
@ -134,6 +135,17 @@ public class SongBottomSheetViewModel extends AndroidViewModel {
return instantMix; 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<Share> shareTrack() { public MutableLiveData<Share> shareTrack() {
return sharingRepository.createShare(song.getId(), song.getTitle(), null); return sharingRepository.createShare(song.getId(), song.getTitle(), null);
} }