feat: implemented load queue, adding logging

This commit is contained in:
eddyizm 2025-11-27 13:28:07 -08:00
parent 27f5a47cc9
commit 1ff0b83a19
No known key found for this signature in database
GPG key ID: CF5F671829E8158A
4 changed files with 92 additions and 25 deletions

View file

@ -1,8 +1,11 @@
package com.cappielloantonio.tempo.repository; package com.cappielloantonio.tempo.repository;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.App;
import com.cappielloantonio.tempo.database.AppDatabase; import com.cappielloantonio.tempo.database.AppDatabase;
@ -52,6 +55,8 @@ public class QueueRepository {
public MutableLiveData<PlayQueue> getPlayQueue() { public MutableLiveData<PlayQueue> getPlayQueue() {
MutableLiveData<PlayQueue> playQueue = new MutableLiveData<>(); MutableLiveData<PlayQueue> playQueue = new MutableLiveData<>();
Log.d(TAG, "Getting play queue from server...");
App.getSubsonicClientInstance(false) App.getSubsonicClientInstance(false)
.getBookmarksClient() .getBookmarksClient()
.getPlayQueue() .getPlayQueue()
@ -59,12 +64,19 @@ public class QueueRepository {
@Override @Override
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) { public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlayQueue() != null) { if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlayQueue() != null) {
playQueue.setValue(response.body().getSubsonicResponse().getPlayQueue()); PlayQueue serverQueue = response.body().getSubsonicResponse().getPlayQueue();
Log.d(TAG, "Server returned play queue with " +
(serverQueue.getEntries() != null ? serverQueue.getEntries().size() : 0) + " items");
playQueue.setValue(serverQueue);
} else {
Log.d(TAG, "Server returned no play queue");
playQueue.setValue(null);
} }
} }
@Override @Override
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) { public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
Log.e(TAG, "Failed to get play queue", t);
playQueue.setValue(null); playQueue.setValue(null);
} }
}); });
@ -73,18 +85,24 @@ public class QueueRepository {
} }
public void savePlayQueue(List<String> ids, String current, long position) { public void savePlayQueue(List<String> ids, String current, long position) {
Log.d(TAG, "Saving play queue to server - Items: " + ids.size() + ", Current: " + current);
App.getSubsonicClientInstance(false) App.getSubsonicClientInstance(false)
.getBookmarksClient() .getBookmarksClient()
.savePlayQueue(ids, current, position) .savePlayQueue(ids, current, position)
.enqueue(new Callback<ApiResponse>() { .enqueue(new Callback<ApiResponse>() {
@Override @Override
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) { public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
if (response.isSuccessful()) {
Log.d(TAG, "Play queue saved successfully");
} else {
Log.d(TAG, "Play queue save failed with code: " + response.code());
}
} }
@Override @Override
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) { public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
Log.e(TAG, "Play queue save failed", t);
} }
}); });
} }
@ -123,7 +141,6 @@ public class QueueRepository {
private boolean isMediaInQueue(List<Queue> queue, Child media) { private boolean isMediaInQueue(List<Queue> queue, Child media) {
if (queue == null || media == null) return false; if (queue == null || media == null) return false;
return queue.stream().anyMatch(queueItem -> return queue.stream().anyMatch(queueItem ->
queueItem != null && media.getId() != null && queueItem != null && media.getId() != null &&
queueItem.getId().equals(media.getId()) queueItem.getId().equals(media.getId())

View file

@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.fragment;
import android.content.ComponentName; import android.content.ComponentName;
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;
@ -11,6 +12,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.Observer;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaBrowser; import androidx.media3.session.MediaBrowser;
import androidx.media3.session.SessionToken; import androidx.media3.session.SessionToken;
@ -23,9 +25,11 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
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.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.subsonic.models.PlayQueue;
import com.cappielloantonio.tempo.ui.adapter.PlayerSongQueueAdapter; import com.cappielloantonio.tempo.ui.adapter.PlayerSongQueueAdapter;
import com.cappielloantonio.tempo.ui.dialog.PlaylistChooserDialog; import com.cappielloantonio.tempo.ui.dialog.PlaylistChooserDialog;
import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.Preferences;
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel; import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel; import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -46,7 +50,6 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabClearQueue; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabClearQueue;
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabShuffleQueue; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabShuffleQueue;
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabSaveToPlaylist; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabSaveToPlaylist;
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabDownloadAll; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabDownloadAll;
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabLoadQueue; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabLoadQueue;
@ -85,6 +88,11 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
fabDownloadAll.setOnClickListener(v -> handleDownloadAllClick()); fabDownloadAll.setOnClickListener(v -> handleDownloadAllClick());
fabLoadQueue.setOnClickListener(v -> handleLoadQueueClick()); fabLoadQueue.setOnClickListener(v -> handleLoadQueueClick());
// Hide Load Queue FAB if sync is disabled
if (!Preferences.isSyncronizationEnabled()) {
fabLoadQueue.setVisibility(View.GONE);
}
initQueueRecyclerView(); initQueueRecyclerView();
return view; return view;
@ -244,9 +252,12 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
private void toggleFabMenu() { private void toggleFabMenu() {
if (isMenuOpen) { if (isMenuOpen) {
// CLOSE MENU (Reverse order for visual effect) // CLOSE MENU (Reverse order for visual effect)
closeFab(fabSaveToPlaylist, 5); if (Preferences.isSyncronizationEnabled()) {
closeFab(fabDownloadAll, 4); closeFab(fabLoadQueue, 4);
closeFab(fabLoadQueue, 2); }
closeFab(fabSaveToPlaylist, 3);
closeFab(fabDownloadAll, 2);
closeFab(fabClearQueue, 1); closeFab(fabClearQueue, 1);
closeFab(fabShuffleQueue, 0); closeFab(fabShuffleQueue, 0);
@ -255,10 +266,11 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
// OPEN MENU (lowest index at bottom) // OPEN MENU (lowest index at bottom)
openFab(fabShuffleQueue, 0); openFab(fabShuffleQueue, 0);
openFab(fabClearQueue, 1); openFab(fabClearQueue, 1);
openFab(fabLoadQueue, 2); openFab(fabDownloadAll, 2);
openFab(fabDownloadAll, 4); openFab(fabSaveToPlaylist, 3);
openFab(fabSaveToPlaylist, 5); if (Preferences.isSyncronizationEnabled()) {
openFab(fabLoadQueue, 4);
}
fabMenuToggle.animate().rotation(45f).setDuration(ANIMATION_DURATION).start(); fabMenuToggle.animate().rotation(45f).setDuration(ANIMATION_DURATION).start();
} }
isMenuOpen = !isMenuOpen; isMenuOpen = !isMenuOpen;
@ -375,10 +387,46 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
toggleFabMenu(); toggleFabMenu();
} }
private void handleLoadQueueClick() { private void handleLoadQueueClick() {
Log.d(TAG, "Load Queue Clicked! (Placeholder)"); Log.d(TAG, "Load Queue Clicked!");
// Double-check that sync is enabled (shouldn't be visible if disabled, but just in case)
if (!Preferences.isSyncronizationEnabled()) {
toggleFabMenu(); toggleFabMenu();
return;
} }
PlayerBottomSheetViewModel playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
playerBottomSheetViewModel.getPlayQueue().observe(getViewLifecycleOwner(), new Observer<PlayQueue>() {
@Override
public void onChanged(PlayQueue playQueue) {
playerBottomSheetViewModel.getPlayQueue().removeObserver(this);
if (playQueue != null && playQueue.getEntries() != null && !playQueue.getEntries().isEmpty()) {
int currentIndex = 0;
for (int i = 0; i < playQueue.getEntries().size(); i++) {
if (playQueue.getEntries().get(i).getId().equals(playQueue.getCurrent())) {
currentIndex = i;
break;
}
}
MediaManager.startQueue(mediaBrowserListenableFuture, playQueue.getEntries(), currentIndex);
Toast.makeText(requireContext(), "Queue loaded", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(requireContext(), "No saved queue found", Toast.LENGTH_SHORT).show();
}
toggleFabMenu();
}
});
new Handler().postDelayed(() -> {
if (isMenuOpen) {
toggleFabMenu();
}
}, 1000);
}
} }

View file

@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.viewmodel;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.OptIn; import androidx.annotation.OptIn;
@ -12,6 +13,7 @@ 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 androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaBrowser;
import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.interfaces.StarCallback;
import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.model.Download;
@ -291,13 +293,13 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel {
List<String> ids = queue.stream().map(Child::getId).collect(Collectors.toList()); List<String> ids = queue.stream().map(Child::getId).collect(Collectors.toList());
if (media != null) { if (media != null) {
queueRepository.savePlayQueue(ids, media.getId(), 0); // TODO: We need to get the actual playback position here
Log.d(TAG, "Saving play queue - Current: " + media.getId() + ", Items: " + ids.size());
queueRepository.savePlayQueue(ids, media.getId(), 0); // Still hardcoded to 0 for now
return true; return true;
} }
return false; return false;
} }
private void observeCachedLyrics(LifecycleOwner owner, String songId) { private void observeCachedLyrics(LifecycleOwner owner, String songId) {
if (TextUtils.isEmpty(songId)) { if (TextUtils.isEmpty(songId)) {
return; return;

View file

@ -56,7 +56,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:visibility="gone" android:visibility="gone"
android:text="Load Queue (TODO)" android:text="Load Queue"
tools:ignore="HardcodedText" tools:ignore="HardcodedText"
app:icon="@android:drawable/ic_menu_revert" /> app:icon="@android:drawable/ic_menu_revert" />