mirror of
https://github.com/antebudimir/tempus.git
synced 2026-04-15 16:27:26 +00:00
Bug/instant mix issues (#344)
* fix: song bottom sheet changed to livedata and fixed issue * fix: refactor bottom sheet instant mix calls to use livedata.
This commit is contained in:
parent
5ef5731fe3
commit
64a1966ad8
7 changed files with 105 additions and 299 deletions
|
|
@ -7,7 +7,6 @@ import android.util.Log;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.interfaces.DecadesCallback;
|
import com.cappielloantonio.tempo.interfaces.DecadesCallback;
|
||||||
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.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumInfo;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumInfo;
|
||||||
|
|
@ -206,17 +205,12 @@ public class AlbumRepository {
|
||||||
return albumInfo;
|
return albumInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getInstantMix(AlbumID3 album, int count, final MediaCallback callback) {
|
public MutableLiveData<List<Child>> getInstantMix(AlbumID3 album, int count) {
|
||||||
Log.d("AlbumRepository", "Starting Instant Mix for album: " + album.getName());
|
// Delegate to the centralized SongRepository
|
||||||
|
return new SongRepository().getInstantMix(album.getId(), SeedType.ALBUM, count);
|
||||||
new SongRepository().getInstantMix(album.getId(), SeedType.ALBUM, count, songs -> {
|
|
||||||
|
|
||||||
if (songs != null && !songs.isEmpty()) {
|
|
||||||
callback.onLoadMedia(songs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public MutableLiveData<List<Integer>> getDecades() {
|
public MutableLiveData<List<Integer>> getDecades() {
|
||||||
MutableLiveData<List<Integer>> decades = new MutableLiveData<>();
|
MutableLiveData<List<Integer>> decades = new MutableLiveData<>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,18 +293,6 @@ 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<>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.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,7 +24,6 @@ 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.repository.AlbumRepository;
|
import com.cappielloantonio.tempo.repository.AlbumRepository;
|
||||||
import com.cappielloantonio.tempo.service.MediaManager;
|
import com.cappielloantonio.tempo.service.MediaManager;
|
||||||
|
|
@ -61,8 +59,7 @@ 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 isFirstBatch = true;
|
||||||
private boolean dismissalScheduled = false;
|
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
private static final String TAG = "AlbumBottomSheetDialog";
|
private static final String TAG = "AlbumBottomSheetDialog";
|
||||||
|
|
@ -122,65 +119,32 @@ 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;
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
dismissalScheduled = false;
|
if (activity == null) return;
|
||||||
|
|
||||||
|
ListenableFuture<MediaBrowser> activityBrowserFuture = activity.getMediaBrowserListenableFuture();
|
||||||
|
if (activityBrowserFuture == null) return;
|
||||||
|
|
||||||
|
isFirstBatch = true;
|
||||||
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();
|
||||||
final Runnable failsafeTimeout = () -> {
|
|
||||||
if (!playbackStarted && !dismissalScheduled) {
|
albumBottomSheetViewModel.getAlbumInstantMix(activity, album).observe(activity, media -> {
|
||||||
Log.w(TAG, "No response received within 3 seconds");
|
if (media == null || media.isEmpty()) return;
|
||||||
if (isAdded() && getActivity() != null) {
|
if (getActivity() == null) return;
|
||||||
Toast.makeText(getContext(),
|
|
||||||
R.string.bottom_sheet_problem_generating_instant_mix,
|
MusicUtil.ratingFilter(media);
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
|
if (isFirstBatch) {
|
||||||
|
isFirstBatch = false;
|
||||||
|
|
||||||
|
MediaManager.startQueue(activityBrowserFuture, media, 0);
|
||||||
|
activity.setBottomSheetInPeek(true);
|
||||||
|
|
||||||
|
if (isAdded()) {
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
};
|
MediaManager.enqueue(activityBrowserFuture, media, true);
|
||||||
view.postDelayed(failsafeTimeout, 3000);
|
|
||||||
|
|
||||||
new AlbumRepository().getInstantMix(album, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadMedia(List<?> media) {
|
|
||||||
view.removeCallbacks(failsafeTimeout);
|
|
||||||
if (!isAdded() || getActivity() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicUtil.ratingFilter((ArrayList<Child>) media);
|
|
||||||
|
|
||||||
if (!media.isEmpty()) {
|
|
||||||
boolean isFirstBatch = !playbackStarted;
|
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
|
|
||||||
playbackStarted = true;
|
|
||||||
|
|
||||||
if (getActivity() instanceof MainActivity) {
|
|
||||||
((MainActivity) getActivity()).setBottomSheetInPeek(true);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -335,30 +299,4 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ package com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
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;
|
||||||
|
|
@ -19,12 +18,10 @@ 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;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
|
|
@ -32,8 +29,6 @@ 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;
|
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||||
|
|
@ -44,8 +39,7 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
|
|
||||||
private boolean playbackStarted = false;
|
private boolean isFirstBatch = true;
|
||||||
private boolean dismissalScheduled = false;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -95,68 +89,30 @@ 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");
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
|
if (activity == null) return;
|
||||||
|
|
||||||
|
ListenableFuture<MediaBrowser> activityBrowserFuture = activity.getMediaBrowserListenableFuture();
|
||||||
|
if (activityBrowserFuture == null) return;
|
||||||
|
|
||||||
|
isFirstBatch = true;
|
||||||
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();
|
||||||
playbackStarted = false;
|
|
||||||
dismissalScheduled = false;
|
artistBottomSheetViewModel.getArtistInstantMix(activity, artist).observe(activity, media -> {
|
||||||
final Runnable failsafeTimeout = () -> {
|
if (media == null || media.isEmpty()) return;
|
||||||
if (!playbackStarted && !dismissalScheduled) {
|
if (getActivity() == null) return;
|
||||||
Log.w(TAG, "No response received within 3 seconds");
|
|
||||||
if (isAdded() && getActivity() != null) {
|
MusicUtil.ratingFilter(media);
|
||||||
Toast.makeText(getContext(),
|
|
||||||
R.string.bottom_sheet_problem_generating_instant_mix,
|
if (isFirstBatch) {
|
||||||
Toast.LENGTH_SHORT).show();
|
isFirstBatch = false;
|
||||||
|
MediaManager.startQueue(activityBrowserFuture, media, 0);
|
||||||
|
activity.setBottomSheetInPeek(true);
|
||||||
|
if (isAdded()) {
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
};
|
MediaManager.enqueue(activityBrowserFuture, media, true);
|
||||||
view.postDelayed(failsafeTimeout, 3000);
|
|
||||||
|
|
||||||
new ArtistRepository().getInstantMix(artist, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadMedia(List<?> media) {
|
|
||||||
view.removeCallbacks(failsafeTimeout);
|
|
||||||
if (!isAdded() || getActivity() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Received " + media.size() + " songs for artist");
|
|
||||||
|
|
||||||
MusicUtil.ratingFilter((ArrayList<Child>) media);
|
|
||||||
|
|
||||||
if (!media.isEmpty()) {
|
|
||||||
boolean isFirstBatch = !playbackStarted;
|
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
|
|
||||||
playbackStarted = true;
|
|
||||||
|
|
||||||
if (getActivity() instanceof MainActivity) {
|
|
||||||
((MainActivity) getActivity()).setBottomSheetInPeek(true);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -192,30 +148,4 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOffline(Exception exception) {
|
|
||||||
return exception != null && exception.getMessage() != null &&
|
|
||||||
(exception.getMessage().contains("Network") ||
|
|
||||||
exception.getMessage().contains("timeout") ||
|
|
||||||
exception.getMessage().contains("offline"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +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.os.Looper;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -26,7 +24,6 @@ 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;
|
||||||
|
|
@ -48,13 +45,10 @@ import com.google.android.material.chip.Chip;
|
||||||
import com.google.android.material.chip.ChipGroup;
|
import com.google.android.material.chip.ChipGroup;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import androidx.media3.common.MediaItem;
|
|
||||||
import com.cappielloantonio.tempo.util.ExternalAudioWriter;
|
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 {
|
||||||
|
|
@ -72,9 +66,7 @@ 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 isFirstBatch = true;
|
||||||
private boolean dismissalScheduled = false;
|
|
||||||
|
|
||||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||||
private static final String TAG = "SongBottomSheetDialog";
|
private static final String TAG = "SongBottomSheetDialog";
|
||||||
|
|
||||||
|
|
@ -152,68 +144,36 @@ 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 -> {
|
||||||
playbackStarted = false;
|
MainActivity activity = (MainActivity) getActivity();
|
||||||
dismissalScheduled = false;
|
if (activity == null) return;
|
||||||
|
|
||||||
|
ListenableFuture<MediaBrowser> activityBrowserFuture = activity.getMediaBrowserListenableFuture();
|
||||||
|
if (activityBrowserFuture == null) {
|
||||||
|
Log.e(TAG, "MediaBrowser Future is null in MainActivity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirstBatch = true;
|
||||||
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();
|
||||||
|
|
||||||
final Runnable failsafeTimeout = () -> {
|
songBottomSheetViewModel.getInstantMix(activity, song).observe(activity, media -> {
|
||||||
if (!playbackStarted && !dismissalScheduled) {
|
|
||||||
Log.w(TAG, "No response received within 3 seconds");
|
if (media == null || media.isEmpty()) return;
|
||||||
if (isAdded() && getActivity() != null) {
|
if (getActivity() == null) return;
|
||||||
Toast.makeText(getContext(),
|
|
||||||
R.string.bottom_sheet_problem_generating_instant_mix,
|
MusicUtil.ratingFilter(media);
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
|
if (isFirstBatch) {
|
||||||
|
isFirstBatch = false;
|
||||||
|
MediaManager.startQueue(activityBrowserFuture, media, 0);
|
||||||
|
activity.setBottomSheetInPeek(true);
|
||||||
|
if (isAdded()) {
|
||||||
dismissBottomSheet();
|
dismissBottomSheet();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
};
|
MediaManager.enqueue(activityBrowserFuture, media, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadMedia(List<?> media) {
|
|
||||||
view.removeCallbacks(failsafeTimeout);
|
|
||||||
if (!isAdded() || getActivity() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicUtil.ratingFilter((ArrayList<Child>) media);
|
|
||||||
|
|
||||||
if (!media.isEmpty()) {
|
|
||||||
boolean isFirstBatch = !playbackStarted;
|
|
||||||
MediaManager.startQueue(mediaBrowserListenableFuture, (ArrayList<Child>) media, 0);
|
|
||||||
playbackStarted = true;
|
|
||||||
|
|
||||||
if (getActivity() instanceof MainActivity) {
|
|
||||||
((MainActivity) getActivity()).setBottomSheetInPeek(true);
|
|
||||||
}
|
|
||||||
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);
|
TextView playNext = view.findViewById(R.id.play_next_text_view);
|
||||||
|
|
@ -382,16 +342,12 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
chip.setVisibility(View.VISIBLE);
|
chip.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
chip.setOnClickListener(v -> {
|
chip.setOnClickListener(v -> {
|
||||||
if (assetLink != null) {
|
((MainActivity) requireActivity()).openAssetLink(assetLink);
|
||||||
((MainActivity) requireActivity()).openAssetLink(assetLink);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
chip.setOnLongClickListener(v -> {
|
chip.setOnLongClickListener(v -> {
|
||||||
if (assetLink != null) {
|
AssetLinkUtil.copyToClipboard(requireContext(), assetLink);
|
||||||
AssetLinkUtil.copyToClipboard(requireContext(), assetLink);
|
Toast.makeText(requireContext(), getString(R.string.asset_link_copied_toast, id), Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(requireContext(), getString(R.string.asset_link_copied_toast, id), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -453,30 +409,4 @@ public class SongBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
|
|
@ -26,6 +27,7 @@ import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
import com.cappielloantonio.tempo.util.NetworkUtil;
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
@ -36,6 +38,7 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final FavoriteRepository favoriteRepository;
|
private final FavoriteRepository favoriteRepository;
|
||||||
private final SharingRepository sharingRepository;
|
private final SharingRepository sharingRepository;
|
||||||
private AlbumID3 album;
|
private AlbumID3 album;
|
||||||
|
private final MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(null);
|
||||||
|
|
||||||
public AlbumBottomSheetViewModel(@NonNull Application application) {
|
public AlbumBottomSheetViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
|
|
@ -131,4 +134,12 @@ public class AlbumBottomSheetViewModel extends AndroidViewModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Child>> getAlbumInstantMix(LifecycleOwner owner, AlbumID3 album) {
|
||||||
|
instantMix.setValue(Collections.emptyList());
|
||||||
|
|
||||||
|
albumRepository.getInstantMix(album, 20).observe(owner, instantMix::postValue);
|
||||||
|
|
||||||
|
return instantMix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,12 @@ import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
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;
|
||||||
|
|
@ -17,6 +22,7 @@ import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -24,6 +30,7 @@ import java.util.List;
|
||||||
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
private final FavoriteRepository favoriteRepository;
|
private final FavoriteRepository favoriteRepository;
|
||||||
|
private final MutableLiveData<List<Child>> instantMix = new MutableLiveData<>(null);
|
||||||
|
|
||||||
private ArtistID3 artist;
|
private ArtistID3 artist;
|
||||||
|
|
||||||
|
|
@ -95,6 +102,7 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
Log.d("ArtistSync", "Starting artist sync for: " + artist.getName());
|
Log.d("ArtistSync", "Starting artist sync for: " + artist.getName());
|
||||||
|
|
||||||
artistRepository.getArtistAllSongs(artist.getId(), new ArtistRepository.ArtistSongsCallback() {
|
artistRepository.getArtistAllSongs(artist.getId(), new ArtistRepository.ArtistSongsCallback() {
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
@Override
|
@Override
|
||||||
public void onSongsCollected(List<Child> songs) {
|
public void onSongsCollected(List<Child> songs) {
|
||||||
Log.d("ArtistSync", "Callback triggered with songs: " + (songs != null ? songs.size() : 0));
|
Log.d("ArtistSync", "Callback triggered with songs: " + (songs != null ? songs.size() : 0));
|
||||||
|
|
@ -114,5 +122,12 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
Log.d("ArtistSync", "Artist sync preference is disabled");
|
Log.d("ArtistSync", "Artist sync preference is disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///
|
|
||||||
|
public LiveData<List<Child>> getArtistInstantMix(LifecycleOwner owner, ArtistID3 artist) {
|
||||||
|
instantMix.setValue(Collections.emptyList());
|
||||||
|
|
||||||
|
artistRepository.getInstantMix(artist, 20).observe(owner, instantMix::postValue);
|
||||||
|
|
||||||
|
return instantMix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue