wip: added fab, need to implement actions

This commit is contained in:
eddyizm 2025-11-24 11:36:56 -08:00
parent 97d1b408e1
commit 38fb2c69f1
No known key found for this signature in database
GPG key ID: CF5F671829E8158A
2 changed files with 228 additions and 40 deletions

View file

@ -39,6 +39,20 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
private InnerFragmentPlayerQueueBinding bind;
private com.google.android.material.floatingactionbutton.FloatingActionButton fabMenuToggle;
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 fabSaveQueue;
private com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton fabLoadQueue;
private boolean isMenuOpen = false;
private final int ANIMATION_DURATION = 250;
private final float FAB_VERTICAL_SPACING_DP = 70f;
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
private PlaybackViewModel playbackViewModel;
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
@ -53,6 +67,24 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
playbackViewModel = new ViewModelProvider(requireActivity()).get(PlaybackViewModel.class);
fabMenuToggle = bind.fabMenuToggle;
fabClearQueue = bind.fabClearQueue;
fabShuffleQueue = bind.fabShuffleQueue;
fabSaveToPlaylist = bind.fabSaveToPlaylist;
fabDownloadAll = bind.fabDownloadAll;
fabSaveQueue = bind.fabSaveQueue;
fabLoadQueue = bind.fabLoadQueue;
fabMenuToggle.setOnClickListener(v -> toggleFabMenu());
fabClearQueue.setOnClickListener(v -> handleClearQueueClick());
fabShuffleQueue.setOnClickListener(v -> handleShuffleQueueClick());
fabSaveToPlaylist.setOnClickListener(v -> handleSaveToPlaylistClick());
fabDownloadAll.setOnClickListener(v -> handleDownloadAllClick());
fabSaveQueue.setOnClickListener(v -> handleSaveQueueClick());
fabLoadQueue.setOnClickListener(v -> handleLoadQueueClick());
initQueueRecyclerView();
return view;
@ -109,8 +141,8 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
mediaBrowserListenableFuture.addListener(() -> {
try {
MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get();
initShuffleButton(mediaBrowser);
initCleanButton(mediaBrowser);
// initShuffleButton(mediaBrowser);
// initCleanButton(mediaBrowser);
} catch (Exception exception) {
exception.printStackTrace();
}
@ -188,45 +220,45 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
}).attachToRecyclerView(bind.playerQueueRecyclerView);
}
private void initShuffleButton(MediaBrowser mediaBrowser) {
bind.playerShuffleQueueFab.setOnClickListener(view -> {
int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
int endPosition = playerSongQueueAdapter.getItems().size() - 1;
// private void initShuffleButton(MediaBrowser mediaBrowser) {
// bind.playerShuffleQueueFab.setOnClickListener(view -> {
// int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
// int endPosition = playerSongQueueAdapter.getItems().size() - 1;
if (startPosition < endPosition) {
ArrayList<Integer> pool = new ArrayList<>();
// if (startPosition < endPosition) {
// ArrayList<Integer> pool = new ArrayList<>();
for (int i = startPosition; i <= endPosition; i++) {
pool.add(i);
}
// for (int i = startPosition; i <= endPosition; i++) {
// pool.add(i);
// }
while (pool.size() >= 2) {
int fromPosition = (int) (Math.random() * (pool.size()));
int positionA = pool.get(fromPosition);
pool.remove(fromPosition);
// while (pool.size() >= 2) {
// int fromPosition = (int) (Math.random() * (pool.size()));
// int positionA = pool.get(fromPosition);
// pool.remove(fromPosition);
int toPosition = (int) (Math.random() * (pool.size()));
int positionB = pool.get(toPosition);
pool.remove(toPosition);
// int toPosition = (int) (Math.random() * (pool.size()));
// int positionB = pool.get(toPosition);
// pool.remove(toPosition);
Collections.swap(playerSongQueueAdapter.getItems(), positionA, positionB);
bind.playerQueueRecyclerView.getAdapter().notifyItemMoved(positionA, positionB);
}
// Collections.swap(playerSongQueueAdapter.getItems(), positionA, positionB);
// bind.playerQueueRecyclerView.getAdapter().notifyItemMoved(positionA, positionB);
// }
MediaManager.shuffle(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
}
});
}
// MediaManager.shuffle(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
// }
// });
// }
private void initCleanButton(MediaBrowser mediaBrowser) {
bind.playerCleanQueueButton.setOnClickListener(view -> {
int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
int endPosition = playerSongQueueAdapter.getItems().size();
// private void initCleanButton(MediaBrowser mediaBrowser) {
// bind.playerCleanQueueButton.setOnClickListener(view -> {
// int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
// int endPosition = playerSongQueueAdapter.getItems().size();
MediaManager.removeRange(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
bind.playerQueueRecyclerView.getAdapter().notifyItemRangeRemoved(startPosition, endPosition);
});
}
// MediaManager.removeRange(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
// bind.playerQueueRecyclerView.getAdapter().notifyItemRangeRemoved(startPosition, endPosition);
// });
// }
private void updateNowPlayingItem() {
playerSongQueueAdapter.notifyDataSetChanged();
@ -259,4 +291,90 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
playerSongQueueAdapter.setPlaybackState(id, playing != null && playing);
}
}
/**
* Toggles the visibility and animates all six secondary FABs.
*/
private void toggleFabMenu() {
if (isMenuOpen) {
// CLOSE MENU (Reverse order for visual effect)
closeFab(fabSaveToPlaylist, 5);
closeFab(fabDownloadAll, 4);
closeFab(fabSaveQueue, 3);
closeFab(fabLoadQueue, 2);
closeFab(fabClearQueue, 1);
closeFab(fabShuffleQueue, 0);
fabMenuToggle.animate().rotation(0f).setDuration(ANIMATION_DURATION).start();
} else {
// OPEN MENU (lowest index at bottom)
openFab(fabShuffleQueue, 0);
openFab(fabClearQueue, 1);
openFab(fabLoadQueue, 2);
openFab(fabSaveQueue, 3);
openFab(fabDownloadAll, 4);
openFab(fabSaveToPlaylist, 5);
fabMenuToggle.animate().rotation(45f).setDuration(ANIMATION_DURATION).start();
}
isMenuOpen = !isMenuOpen;
}
private void openFab(View fab, int index) {
final float displacement = getResources().getDisplayMetrics().density * (FAB_VERTICAL_SPACING_DP * (index + 1));
fab.setVisibility(View.VISIBLE);
fab.setAlpha(0f);
fab.setTranslationY(displacement); // Start at the hidden (closed) position
fab.animate()
.translationY(0f)
.alpha(1f)
.setDuration(ANIMATION_DURATION)
.start();
}
private void closeFab(View fab, int index) {
final float displacement = getResources().getDisplayMetrics().density * (FAB_VERTICAL_SPACING_DP * (index + 1));
fab.animate()
.translationY(displacement)
.alpha(0f)
.setDuration(ANIMATION_DURATION)
.withEndAction(() -> fab.setVisibility(View.GONE))
.start();
}
private void handleShuffleQueueClick() {
Log.d(TAG, "Shuffle Queue Clicked!");
toggleFabMenu();
// TODO: Insert existing shuffle logic here
}
private void handleClearQueueClick() {
Log.d(TAG, "Clear Queue Clicked!");
toggleFabMenu();
// TODO: Insert existing clear queue logic here
}
private void handleSaveToPlaylistClick() {
Log.d(TAG, "Save to Playlist Clicked! (Placeholder)");
toggleFabMenu();
}
private void handleDownloadAllClick() {
Log.d(TAG, "Download All Clicked! (Placeholder)");
toggleFabMenu();
}
private void handleSaveQueueClick() {
Log.d(TAG, "Save Queue Clicked! (Placeholder)");
toggleFabMenu();
}
private void handleLoadQueueClick() {
Log.d(TAG, "Load Queue Clicked! (Placeholder)");
toggleFabMenu();
}
}

View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -27,14 +29,82 @@
</com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/player_shuffle_queue_fab"
<LinearLayout
android:id="@+id/fab_menu_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="end"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/content_description_shuffle_button"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="@drawable/ic_shuffle" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_save_to_playlist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="Save to Playlist (TODO)"
tools:ignore="HardcodedText"
app:icon="@android:drawable/ic_menu_edit" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_download_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="Download All (TODO)"
tools:ignore="HardcodedText"
app:icon="@android:drawable/stat_sys_download_done" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_save_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="Save Queue (TODO)"
tools:ignore="HardcodedText"
app:icon="@android:drawable/ic_menu_save" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_load_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="Load Queue (TODO)"
tools:ignore="HardcodedText"
app:icon="@android:drawable/ic_menu_revert" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_clear_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="@string/player_queue_clean_all_button"
app:icon="@android:drawable/ic_menu_delete" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_shuffle_queue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:visibility="gone"
android:text="@string/content_description_shuffle_button"
app:icon="@drawable/ic_shuffle" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_menu_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Toggle action menu (TODO: Fix Localization)"
tools:ignore="HardcodedText"
app:srcCompat="@drawable/ic_add" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>