fix: adde a scheduled delay to allow callbacks to succeed

This commit is contained in:
eddyizm 2026-01-04 09:27:53 -08:00
parent a2801f3168
commit 993374e56c
No known key found for this signature in database
GPG key ID: CF5F671829E8158A
4 changed files with 122 additions and 46 deletions

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData;
import android.util.Log; import android.util.Log;
import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.App;
import com.cappielloantonio.tempo.interfaces.MediaCallback;
import com.cappielloantonio.tempo.subsonic.base.ApiResponse; import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
import com.cappielloantonio.tempo.subsonic.models.ArtistID3; import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
import com.cappielloantonio.tempo.subsonic.models.AlbumID3; import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
@ -292,6 +293,18 @@ public class ArtistRepository {
return new SongRepository().getInstantMix(artist.getId(), SeedType.ARTIST, count); 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<List<Child>> getRandomSong(ArtistID3 artist, int count) { public MutableLiveData<List<Child>> getRandomSong(ArtistID3 artist, int count) {
MutableLiveData<List<Child>> randomSongs = new MutableLiveData<>(); MutableLiveData<List<Child>> randomSongs = new MutableLiveData<>();

View file

@ -5,7 +5,6 @@ import android.content.ClipboardManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -62,6 +61,9 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
private List<Child> currentAlbumTracks = Collections.emptyList(); private List<Child> currentAlbumTracks = Collections.emptyList();
private List<MediaItem> currentAlbumMediaItems = Collections.emptyList(); private List<MediaItem> currentAlbumMediaItems = Collections.emptyList();
private boolean playbackStarted = false;
private boolean dismissalScheduled = false;
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture; private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
private static final String TAG = "AlbumBottomSheetDialog"; 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); TextView playRadio = view.findViewById(R.id.play_radio_text_view);
playRadio.setOnClickListener(v -> { playRadio.setOnClickListener(v -> {
playbackStarted = false;
dismissalScheduled = false;
Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show(); Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show();
new AlbumRepository().getInstantMix(album, 20, new MediaCallback() { new AlbumRepository().getInstantMix(album, 20, new MediaCallback() {
@Override @Override
public void onError(Exception exception) { public void onError(Exception exception) {
Log.e(TAG, "Error: " + exception.getMessage()); Log.e(TAG, "Error: " + exception.getMessage());
if (!playbackStarted && !dismissalScheduled) {
scheduleDelayedDismissal(v);
}
} }
@Override @Override
@ -136,30 +143,26 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
MusicUtil.ratingFilter((ArrayList<Child>) media); MusicUtil.ratingFilter((ArrayList<Child>) media);
if (!media.isEmpty()) { if (!media.isEmpty()) {
boolean isFirstBatch = !playbackStarted;
MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0); MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
playbackStarted = true;
if (getActivity() instanceof MainActivity) { if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).setBottomSheetInPeek(true); ((MainActivity) getActivity()).setBottomSheetInPeek(true);
} }
} if (isFirstBatch && !dismissalScheduled) {
scheduleDelayedDismissal(v);
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(), 300); } else {
}, 300); if (!playbackStarted && !dismissalScheduled) {
scheduleDelayedDismissal(v);
}
}
} }
}); });
}); });
TextView playRandom = view.findViewById(R.id.play_random_text_view); TextView playRandom = view.findViewById(R.id.play_random_text_view);
playRandom.setOnClickListener(v -> { playRandom.setOnClickListener(v -> {
AlbumRepository albumRepository = new AlbumRepository(); AlbumRepository albumRepository = new AlbumRepository();
@ -308,4 +311,24 @@ public class AlbumBottomSheetDialog 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);
}
} }

View file

@ -19,6 +19,7 @@ import androidx.media3.session.SessionToken;
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.repository.ArtistRepository; import com.cappielloantonio.tempo.repository.ArtistRepository;
import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.service.MediaManager;
import com.cappielloantonio.tempo.service.MediaService; 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.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@UnstableApi @UnstableApi
@ -42,6 +44,9 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture; private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
private boolean playbackStarted = false;
private boolean dismissalScheduled = false;
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 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); TextView playRadio = view.findViewById(R.id.play_radio_text_view);
playRadio.setOnClickListener(v -> { playRadio.setOnClickListener(v -> {
Log.d(TAG, "Artist instant mix clicked"); Log.d(TAG, "Artist instant mix clicked");
Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_LONG).show(); Toast.makeText(requireContext(), R.string.bottom_sheet_generating_instant_mix, Toast.LENGTH_SHORT).show();
ArtistRepository artistRepository = new ArtistRepository(); playbackStarted = false;
artistRepository.getInstantMix(artist, 20) dismissalScheduled = false;
.observe(getViewLifecycleOwner(), new androidx.lifecycle.Observer<List<Child>>() {
@Override new ArtistRepository().getInstantMix(artist, 20, new MediaCallback() {
public void onChanged(List<Child> songs) { @Override
if (songs != null && !songs.isEmpty()) { public void onError(Exception exception) {
Log.d(TAG, "Starting queue with " + songs.size() + " songs"); Log.e(TAG, "Error: " + exception.getMessage());
MusicUtil.ratingFilter(songs);
MediaManager.startQueue(mediaBrowserListenableFuture, songs, 0); if (!playbackStarted && !dismissalScheduled) {
((MainActivity) requireActivity()).setBottomSheetInPeek(true); scheduleDelayedDismissal(v);
artistRepository.getInstantMix(artist, 20) }
.removeObserver(this); }
view.postDelayed(() -> {
try { @Override
if (mediaBrowserListenableFuture.isDone()) { public void onLoadMedia(List<?> media) {
MediaBrowser browser = mediaBrowserListenableFuture.get(); if (!isAdded() || getActivity() == null) {
if (browser != null && browser.isPlaying()) { return;
dismissBottomSheet(); }
return;
} Log.d(TAG, "Received " + media.size() + " songs for artist");
}
} catch (Exception e) { MusicUtil.ratingFilter((ArrayList<Child>) media);
// Ignore
} if (!media.isEmpty()) {
view.postDelayed(() -> dismissBottomSheet(), 300); boolean isFirstBatch = !playbackStarted;
}, 300); MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) 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); TextView playRandom = view.findViewById(R.id.play_random_text_view);
@ -153,4 +170,25 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
private void releaseMediaBrowser() { private void releaseMediaBrowser() {
MediaBrowser.releaseFuture(mediaBrowserListenableFuture); MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
} }
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);
}
} }

View file

@ -4,10 +4,12 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.OptIn;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.media3.common.util.UnstableApi;
import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.model.Download;
import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.interfaces.StarCallback;
@ -33,7 +35,6 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
private final ArtistRepository artistRepository; private final ArtistRepository artistRepository;
private final FavoriteRepository favoriteRepository; private final FavoriteRepository favoriteRepository;
private final SharingRepository sharingRepository; private final SharingRepository sharingRepository;
private AlbumID3 album; private AlbumID3 album;
public AlbumBottomSheetViewModel(@NonNull Application application) { public AlbumBottomSheetViewModel(@NonNull Application application) {
@ -116,6 +117,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
MutableLiveData<List<Child>> tracksLiveData = albumRepository.getAlbumTracks(album.getId()); MutableLiveData<List<Child>> tracksLiveData = albumRepository.getAlbumTracks(album.getId());
tracksLiveData.observeForever(new Observer<List<Child>>() { tracksLiveData.observeForever(new Observer<List<Child>>() {
@OptIn(markerClass = UnstableApi.class)
@Override @Override
public void onChanged(List<Child> songs) { public void onChanged(List<Child> songs) {
if (songs != null && !songs.isEmpty()) { if (songs != null && !songs.isEmpty()) {