From 2c783beabad0a8f368b63b44d2d407f758721b57 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Tue, 10 Nov 2020 19:58:53 -0700 Subject: [PATCH] Redesign headers/menus Redesign the header items so they look nicer, update the PopupMenus with some new things as well. --- .../java/org/oxycblt/auxio/MainActivity.kt | 1 + .../auxio/detail/AlbumDetailFragment.kt | 32 ++--- .../detail/adapters/DetailSongAdapter.kt | 6 +- .../oxycblt/auxio/library/LibraryFragment.kt | 7 +- .../auxio/playback/PlaybackViewModel.kt | 6 +- .../auxio/playback/queue/QueueFragment.kt | 10 +- .../org/oxycblt/auxio/songs/SongsFragment.kt | 7 +- .../org/oxycblt/auxio/ui/InterfaceUtils.kt | 109 +++++++++++------- .../{ic_user_queue.xml => ic_queue_add.xml} | 0 .../main/res/layout/fragment_album_detail.xml | 6 +- .../res/layout/fragment_artist_detail.xml | 23 ++-- .../main/res/layout/fragment_genre_detail.xml | 11 +- app/src/main/res/layout/fragment_queue.xml | 1 + app/src/main/res/layout/item_header.xml | 7 +- .../main/res/menu/menu_album_song_actions.xml | 11 ++ app/src/main/res/menu/menu_song_actions.xml | 2 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 2 +- 18 files changed, 149 insertions(+), 93 deletions(-) rename app/src/main/res/drawable/{ic_user_queue.xml => ic_queue_add.xml} (100%) create mode 100644 app/src/main/res/menu/menu_album_song_actions.xml diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 4ba9c0091..018c263c9 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -13,6 +13,7 @@ import org.oxycblt.auxio.ui.accent // lead to nothing being displayed [Possibly Un-fixable] // FIXME: Compat issues with Versions 5/6 that cause recyclerview // dividers not to show and for progress bars to look wonky +// FIXME: Navigation memory leak that is really confusing class MainActivity : AppCompatActivity(R.layout.activity_main) { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 13df0c5e6..4e3fbba5a 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -5,6 +5,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.PopupMenu import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController @@ -17,6 +18,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.ui.applyDivider import org.oxycblt.auxio.ui.disable +import org.oxycblt.auxio.ui.setupAlbumSongActions class AlbumDetailFragment : Fragment() { @@ -36,7 +38,6 @@ class AlbumDetailFragment : Fragment() { if (detailModel.currentAlbum.value == null || detailModel.currentAlbum.value?.id != args.albumId ) { - detailModel.updateAlbum( MusicStore.getInstance().albums.find { it.id == args.albumId @@ -44,9 +45,16 @@ class AlbumDetailFragment : Fragment() { ) } - val songAdapter = DetailSongAdapter { - playbackModel.playSong(it, PlaybackMode.IN_ALBUM) - } + val songAdapter = DetailSongAdapter( + { + playbackModel.playSong(it, PlaybackMode.IN_ALBUM) + }, + { data, view -> + PopupMenu(requireContext(), view).setupAlbumSongActions( + data, requireContext(), detailModel, playbackModel + ) + } + ) // --- UI SETUP --- @@ -100,24 +108,20 @@ class AlbumDetailFragment : Fragment() { ) } - // If the album was shown directly from LibraryFragment, Then enable the ability to - // navigate upwards to the parent artist - if (args.enableParentNav) { - detailModel.doneWithNavToParent() + detailModel.doneWithNavToParent() - detailModel.navToParent.observe(viewLifecycleOwner) { - if (it) { + detailModel.navToParent.observe(viewLifecycleOwner) { + if (it) { + if (!args.enableParentNav) { + findNavController().navigateUp() + } else { findNavController().navigate( AlbumDetailFragmentDirections.actionShowParentArtist( detailModel.currentAlbum.value!!.artist.id ) ) - - detailModel.doneWithNavToParent() } } - - binding.albumArtist.setBackgroundResource(R.drawable.ui_ripple) } Log.d(this::class.simpleName, "Fragment created.") diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt index b0fbce423..4b6d588c7 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt @@ -1,6 +1,7 @@ package org.oxycblt.auxio.detail.adapters import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import org.oxycblt.auxio.databinding.ItemAlbumSongBinding @@ -9,7 +10,8 @@ import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder class DetailSongAdapter( - private val doOnClick: (data: Song) -> Unit + private val doOnClick: (data: Song) -> Unit, + private val doOnLongClick: (data: Song, view: View) -> Unit ) : ListAdapter(DiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( @@ -24,7 +26,7 @@ class DetailSongAdapter( // Generic ViewHolder for a song inner class ViewHolder( private val binding: ItemAlbumSongBinding, - ) : BaseViewHolder(binding, doOnClick, null) { + ) : BaseViewHolder(binding, doOnClick, doOnLongClick) { override fun onBind(data: Song) { binding.song = data diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index 4a59b4dc0..bdf60282d 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.view.forEach @@ -30,7 +31,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.ui.applyColor import org.oxycblt.auxio.ui.applyDivider import org.oxycblt.auxio.ui.resolveAttr -import org.oxycblt.auxio.ui.showActionMenuForSong +import org.oxycblt.auxio.ui.setupSongActions // A Fragment to show all the music in the Library. class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { @@ -57,7 +58,9 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { }, { data, view -> if (data is Song) { - showActionMenuForSong(requireContext(), data, view, playbackModel) + PopupMenu(requireContext(), view).setupSongActions( + data, requireContext(), playbackModel + ) } } ) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index e6fd9ecc2..ab824a049 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -173,25 +173,27 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { } // Move queue OR user queue items, given QueueAdapter indices. - // I have no idea what is going on in this function, but it works, so fun moveQueueItems(adapterFrom: Int, adapterTo: Int): Boolean { var from = adapterFrom.dec() var to = adapterTo.dec() if (from < mUserQueue.value!!.size) { - + // Ignore invalid movements to out of bounds, header, or queue positions if (to >= mUserQueue.value!!.size || to < 0) return false playbackManager.moveUserQueueItems(from, to) } else { + // Ignore invalid movements to out of bounds or header positions if (to < 0) return false + // Get the real queue positions from the nextInQueue positions val delta = mQueue.value!!.size - nextItemsInQueue.value!!.size from += delta to += delta if (userQueue.value!!.isNotEmpty()) { + // Ignore user queue positions if (to <= mUserQueue.value!!.size.inc()) return false from -= mUserQueue.value!!.size.inc() diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index 05174f909..fd044979c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -44,6 +44,10 @@ class QueueFragment : Fragment() { } playbackModel.userQueue.observe(viewLifecycleOwner) { + if (it.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) { + findNavController().navigateUp() + } + queueAdapter.submitList(createQueueDisplay()) { binding.queueRecycler.scrollToPosition(0) scrollRecyclerIfNeeded(binding) @@ -51,6 +55,10 @@ class QueueFragment : Fragment() { } playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { + if (it.isEmpty() && playbackModel.userQueue.value!!.isEmpty()) { + findNavController().navigateUp() + } + queueAdapter.submitList(createQueueDisplay()) { scrollRecyclerIfNeeded(binding) } @@ -87,7 +95,7 @@ class QueueFragment : Fragment() { private fun scrollRecyclerIfNeeded(binding: FragmentQueueBinding) { if ((binding.queueRecycler.layoutManager as LinearLayoutManager) - .findFirstVisibleItemPosition() < 1 + .findFirstVisibleItemPosition() < 1 ) { binding.queueRecycler.scrollToPosition(0) } diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 4044e7661..4967973cc 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -5,6 +5,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.PopupMenu import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import org.oxycblt.auxio.R @@ -13,7 +14,7 @@ import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.ui.applyDivider -import org.oxycblt.auxio.ui.showActionMenuForSong +import org.oxycblt.auxio.ui.setupSongActions class SongsFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() @@ -46,7 +47,9 @@ class SongsFragment : Fragment() { playbackModel.playSong(it, PlaybackMode.ALL_SONGS) }, { data, view -> - showActionMenuForSong(requireContext(), data, view, playbackModel) + PopupMenu(requireContext(), view).setupSongActions( + data, requireContext(), playbackModel + ) } ) applyDivider() diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index 249d809b5..487d40d18 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -6,61 +6,20 @@ import android.graphics.drawable.ColorDrawable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.MenuItem -import android.view.View import android.widget.ImageButton -import android.widget.PopupMenu import android.widget.Toast import androidx.annotation.ColorInt +import androidx.appcompat.widget.PopupMenu import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.R +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.state.PlaybackMode // Functions for managing UI elements [Not Colors] -fun showActionMenuForSong( - context: Context, - song: Song, - view: View, - playbackModel: PlaybackViewModel -) { - PopupMenu(context, view).apply { - inflate(R.menu.menu_song_actions) - setOnMenuItemClickListener { - return@setOnMenuItemClickListener when (it.itemId) { - R.id.action_queue_add -> { - playbackModel.addToUserQueue(song) - - Toast.makeText( - context, - context.getString(R.string.label_queue_added), - Toast.LENGTH_SHORT - ).show() - - true - } - - R.id.action_play_artist -> { - playbackModel.playSong(song, PlaybackMode.IN_ARTIST) - - true - } - - R.id.action_play_album -> { - playbackModel.playSong(song, PlaybackMode.IN_ALBUM) - - true - } - - else -> false - } - } - show() - } -} - // Apply a color to a Menu Item fun MenuItem.applyColor(@ColorInt color: Int) { SpannableString(title).apply { @@ -95,3 +54,67 @@ fun RecyclerView.applyDivider() { addItemDecoration(div) } + +fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) { + inflate(R.menu.menu_song_actions) + setOnMenuItemClickListener { + when (it.itemId) { + R.id.action_queue_add -> { + playbackModel.addToUserQueue(song) + + Toast.makeText( + context, + context.getString(R.string.label_queue_added), + Toast.LENGTH_SHORT + ).show() + + true + } + + R.id.action_play_artist -> { + playbackModel.playSong(song, PlaybackMode.IN_ARTIST) + true + } + + R.id.action_play_album -> { + playbackModel.playSong(song, PlaybackMode.IN_ALBUM) + true + } + + else -> false + } + } + show() +} + +fun PopupMenu.setupAlbumSongActions( + song: Song, + context: Context, + detailViewModel: DetailViewModel, + playbackModel: PlaybackViewModel +) { + inflate(R.menu.menu_album_song_actions) + setOnMenuItemClickListener { + when (it.itemId) { + R.id.action_queue_add -> { + playbackModel.addToUserQueue(song) + + Toast.makeText( + context, + context.getString(R.string.label_queue_added), + Toast.LENGTH_SHORT + ).show() + + true + } + + R.id.action_go_artist -> { + detailViewModel.doNavToParent() + true + } + + else -> false + } + } + show() +} diff --git a/app/src/main/res/drawable/ic_user_queue.xml b/app/src/main/res/drawable/ic_queue_add.xml similarity index 100% rename from app/src/main/res/drawable/ic_user_queue.xml rename to app/src/main/res/drawable/ic_queue_add.xml diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index 81d112c49..98577e32c 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -112,14 +112,14 @@ android:layout_height="match_parent" android:layout_marginTop="@dimen/padding_medium" android:background="@drawable/ui_header_dividers" - android:fontFamily="@font/inter_bold" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@font/inter_semibold" android:paddingStart="@dimen/padding_medium" android:paddingTop="@dimen/padding_small" android:paddingEnd="@dimen/padding_small" android:paddingBottom="@dimen/padding_small" android:text="@string/label_songs" - android:textAppearance="@style/TextAppearance.MaterialComponents.Overline" - android:textSize="16sp" + android:textSize="19sp" app:layout_constraintTop_toBottomOf="@+id/album_details" /> + app:layout_constraintTop_toBottomOf="@+id/artist_counts" + tools:src="@drawable/ic_sort_numeric_down" /> diff --git a/app/src/main/res/menu/menu_album_song_actions.xml b/app/src/main/res/menu/menu_album_song_actions.xml new file mode 100644 index 000000000..e84e868bd --- /dev/null +++ b/app/src/main/res/menu/menu_album_song_actions.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_song_actions.xml b/app/src/main/res/menu/menu_song_actions.xml index 018077ecd..3856ad1e7 100644 --- a/app/src/main/res/menu/menu_song_actions.xml +++ b/app/src/main/res/menu/menu_song_actions.xml @@ -3,7 +3,7 @@ + android:icon="@drawable/ic_queue_add" /> Play Play from artist Play from album + Go to artist Queue Add to queue Added to queue diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bd5ff68b7..73600e581 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -8,7 +8,7 @@ @drawable/ui_cursor true - @style/Widget.CustomPopup + @style/Widget.CustomPopup @color/control_color