diff --git a/app/src/main/java/com/cappielloantonio/tempo/repository/QueueRepository.java b/app/src/main/java/com/cappielloantonio/tempo/repository/QueueRepository.java index 6b3d6252..9d7af95c 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/repository/QueueRepository.java +++ b/app/src/main/java/com/cappielloantonio/tempo/repository/QueueRepository.java @@ -1,8 +1,11 @@ package com.cappielloantonio.tempo.repository; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.database.AppDatabase; @@ -52,6 +55,8 @@ public class QueueRepository { public MutableLiveData getPlayQueue() { MutableLiveData playQueue = new MutableLiveData<>(); + Log.d(TAG, "Getting play queue from server..."); + App.getSubsonicClientInstance(false) .getBookmarksClient() .getPlayQueue() @@ -59,12 +64,19 @@ public class QueueRepository { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { 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 public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Log.e(TAG, "Failed to get play queue", t); playQueue.setValue(null); } }); @@ -73,18 +85,24 @@ public class QueueRepository { } public void savePlayQueue(List ids, String current, long position) { + Log.d(TAG, "Saving play queue to server - Items: " + ids.size() + ", Current: " + current); + App.getSubsonicClientInstance(false) .getBookmarksClient() .savePlayQueue(ids, current, position) .enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { - + if (response.isSuccessful()) { + Log.d(TAG, "Play queue saved successfully"); + } else { + Log.d(TAG, "Play queue save failed with code: " + response.code()); + } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { - + Log.e(TAG, "Play queue save failed", t); } }); } @@ -123,10 +141,9 @@ public class QueueRepository { private boolean isMediaInQueue(List queue, Child media) { if (queue == null || media == null) return false; - - return queue.stream().anyMatch(queueItem -> - queueItem != null && media.getId() != null && - queueItem.getId().equals(media.getId()) + return queue.stream().anyMatch(queueItem -> + queueItem != null && media.getId() != null && + queueItem.getId().equals(media.getId()) ); } @@ -146,8 +163,8 @@ public class QueueRepository { List filteredToAdd = toAdd; final List finalMedia = media; filteredToAdd = toAdd.stream() - .filter(child -> !isMediaInQueue(finalMedia, child)) - .collect(Collectors.toList()); + .filter(child -> !isMediaInQueue(finalMedia, child)) + .collect(Collectors.toList()); for (int i = 0; i < filteredToAdd.size(); i++) { Queue queueItem = new Queue(filteredToAdd.get(i)); diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerQueueFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerQueueFragment.java index 809b1a18..d3e3da65 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerQueueFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerQueueFragment.java @@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.fragment; import android.content.ComponentName; import android.os.Bundle; +import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -11,6 +12,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.Observer; import androidx.media3.common.util.UnstableApi; import androidx.media3.session.MediaBrowser; 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.MediaService; 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.dialog.PlaylistChooserDialog; import com.cappielloantonio.tempo.util.Constants; +import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel; import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel; 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 fabShuffleQueue; - private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabSaveToPlaylist; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabDownloadAll; private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabLoadQueue; @@ -85,6 +88,11 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback { fabDownloadAll.setOnClickListener(v -> handleDownloadAllClick()); fabLoadQueue.setOnClickListener(v -> handleLoadQueueClick()); + // Hide Load Queue FAB if sync is disabled + if (!Preferences.isSyncronizationEnabled()) { + fabLoadQueue.setVisibility(View.GONE); + } + initQueueRecyclerView(); return view; @@ -244,9 +252,12 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback { private void toggleFabMenu() { if (isMenuOpen) { // CLOSE MENU (Reverse order for visual effect) - closeFab(fabSaveToPlaylist, 5); - closeFab(fabDownloadAll, 4); - closeFab(fabLoadQueue, 2); + if (Preferences.isSyncronizationEnabled()) { + closeFab(fabLoadQueue, 4); + } + closeFab(fabSaveToPlaylist, 3); + closeFab(fabDownloadAll, 2); + closeFab(fabClearQueue, 1); closeFab(fabShuffleQueue, 0); @@ -255,10 +266,11 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback { // OPEN MENU (lowest index at bottom) openFab(fabShuffleQueue, 0); openFab(fabClearQueue, 1); - openFab(fabLoadQueue, 2); - openFab(fabDownloadAll, 4); - openFab(fabSaveToPlaylist, 5); - + openFab(fabDownloadAll, 2); + openFab(fabSaveToPlaylist, 3); + if (Preferences.isSyncronizationEnabled()) { + openFab(fabLoadQueue, 4); + } fabMenuToggle.animate().rotation(45f).setDuration(ANIMATION_DURATION).start(); } isMenuOpen = !isMenuOpen; @@ -375,10 +387,46 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback { toggleFabMenu(); } - private void handleLoadQueueClick() { - Log.d(TAG, "Load Queue Clicked! (Placeholder)"); - toggleFabMenu(); - } + 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(); + return; + } + + PlayerBottomSheetViewModel playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class); + + playerBottomSheetViewModel.getPlayQueue().observe(getViewLifecycleOwner(), new Observer() { + @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); + } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java index 2a100fbf..df571690 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java +++ b/app/src/main/java/com/cappielloantonio/tempo/viewmodel/PlayerBottomSheetViewModel.java @@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.viewmodel; import android.app.Application; import android.content.Context; import android.text.TextUtils; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.OptIn; @@ -12,6 +13,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import androidx.media3.common.util.UnstableApi; +import androidx.media3.session.MediaBrowser; import com.cappielloantonio.tempo.interfaces.StarCallback; import com.cappielloantonio.tempo.model.Download; @@ -291,13 +293,13 @@ public class PlayerBottomSheetViewModel extends AndroidViewModel { List ids = queue.stream().map(Child::getId).collect(Collectors.toList()); 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 false; } - private void observeCachedLyrics(LifecycleOwner owner, String songId) { if (TextUtils.isEmpty(songId)) { return; diff --git a/app/src/main/res/layout/inner_fragment_player_queue.xml b/app/src/main/res/layout/inner_fragment_player_queue.xml index 16fb9b44..ecfc8482 100644 --- a/app/src/main/res/layout/inner_fragment_player_queue.xml +++ b/app/src/main/res/layout/inner_fragment_player_queue.xml @@ -56,7 +56,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:visibility="gone" - android:text="Load Queue (TODO)" + android:text="Load Queue" tools:ignore="HardcodedText" app:icon="@android:drawable/ic_menu_revert" />