list: add menu dialog framework

Add a dialog that shows menu information as a bottom sheet instead of as
a PopupMenu.

This did not take as much finessing of BottomSheetBehavior as the main
playback UI framework did, just some style redefinitions.
This commit is contained in:
Alexander Capehart 2023-07-03 14:24:26 -06:00
parent 6dc2892eb9
commit c1158b1a07
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
23 changed files with 359 additions and 39 deletions

View file

@ -38,18 +38,18 @@ import org.oxycblt.auxio.music.info.Name
import org.oxycblt.auxio.music.metadata.AudioProperties
import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.playback.formatDurationMs
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.concatLocalized
import org.oxycblt.auxio.util.logD
/**
* A [ViewBindingDialogFragment] that shows information about a Song.
* A [ViewBindingMaterialDialogFragment] that shows information about a Song.
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
class SongDetailDialog : ViewBindingMaterialDialogFragment<DialogSongDetailBinding>() {
private val detailModel: DetailViewModel by activityViewModels()
// Information about what song to display is initially within the navigation arguments
// as a UID, as that is the only safe way to parcel an song.

View file

@ -33,17 +33,17 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
/**
* A picker [ViewBindingDialogFragment] intended for when the [Artist] to show is ambiguous.
* A picker [ViewBindingMaterialDialogFragment] intended for when the [Artist] to show is ambiguous.
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class ShowArtistDialog :
ViewBindingDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Artist> {
ViewBindingMaterialDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Artist> {
private val detailModel: DetailViewModel by activityViewModels()
private val pickerModel: NavigationPickerViewModel by viewModels()
// Information about what artists to show choices for is initially within the navigation

View file

@ -30,17 +30,18 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogTabsBinding
import org.oxycblt.auxio.home.HomeSettings
import org.oxycblt.auxio.list.EditClickListListener
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.logD
/**
* A [ViewBindingDialogFragment] that allows the user to modify the home [Tab] configuration.
* A [ViewBindingMaterialDialogFragment] that allows the user to modify the home [Tab]
* configuration.
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class TabCustomizeDialog :
ViewBindingDialogFragment<DialogTabsBinding>(), EditClickListListener<Tab> {
ViewBindingMaterialDialogFragment<DialogTabsBinding>(), EditClickListListener<Tab> {
private val tabAdapter = TabAdapter(this)
private var touchHelper: ItemTouchHelper? = null
@Inject lateinit var homeSettings: HomeSettings

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 Auxio Project
* MenuDialogFragment.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.list.menu
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import androidx.core.view.children
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMenuBinding
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
/**
* A [ViewBindingBottomSheetDialogFragment] that displays basic music information and
* a series of options.
* @author Alexander Capehart (OxygenCobalt)
*/
class MenuDialogFragment : ViewBindingBottomSheetDialogFragment<DialogMenuBinding>() {
private val menuAdapter = MenuOptionAdapter()
override fun onCreateBinding(inflater: LayoutInflater) = DialogMenuBinding.inflate(inflater)
override fun onBindingCreated(binding: DialogMenuBinding, savedInstanceState: Bundle?) {
super.onBindingCreated(binding, savedInstanceState)
binding.menuRecycler.apply {
adapter = menuAdapter
itemAnimator = null
}
// Avoid having to use a dummy view and rely on what AndroidX Toolbar uses.
@SuppressLint("RestrictedApi") val builder = MenuBuilder(requireContext())
MenuInflater(requireContext()).inflate(R.menu.item_song, builder)
menuAdapter.update(builder.children.toList(), UpdateInstructions.Diff)
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 Auxio Project
* MenuOptionAdapter.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.list.menu
import android.view.MenuItem
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import org.oxycblt.auxio.databinding.ItemMenuOptionBinding
import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
import org.oxycblt.auxio.util.inflater
/**
* Displays a list of [MenuItem]s as custom list items.
* @author Alexander Capehart (OxygenCobalt)
*/
class MenuOptionAdapter :
FlexibleListAdapter<MenuItem, MenuOptionViewHolder>(MenuOptionViewHolder.DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
MenuOptionViewHolder.from(parent)
override fun onBindViewHolder(holder: MenuOptionViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
/**
* A [DialogRecyclerView.ViewHolder] that displays a list of menu options based on [MenuItem].
* @author Alexander Capehart (OxygenCobalt)
*/
class MenuOptionViewHolder private constructor(private val binding: ItemMenuOptionBinding) :
DialogRecyclerView.ViewHolder(binding.root) {
fun bind(item: MenuItem) {
binding.title.text = item.title
}
companion object {
/**
* Create a new instance.
*
* @param parent The parent to inflate this instance from.
* @return A new instance.
*/
fun from(parent: ViewGroup) =
MenuOptionViewHolder(ItemMenuOptionBinding.inflate(parent.context.inflater))
/** A comparator that can be used with DiffUtil. */
val DIFF_CALLBACK =
object : DiffUtil.ItemCallback<MenuItem>() {
override fun areItemsTheSame(oldItem: MenuItem, newItem: MenuItem) =
oldItem == newItem
override fun areContentsTheSame(oldItem: MenuItem, newItem: MenuItem) =
oldItem.title == newItem.title
}
}
}

View file

@ -22,6 +22,7 @@ import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.annotation.AttrRes
import androidx.core.view.isInvisible
import androidx.core.view.updatePadding
@ -31,6 +32,7 @@ import com.google.android.material.divider.MaterialDivider
import org.oxycblt.auxio.R
import org.oxycblt.auxio.list.recycler.DialogRecyclerView.ViewHolder
import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
* A [RecyclerView] intended for use in Dialogs, adding features such as:
@ -74,6 +76,13 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
invalidateDividers()
}
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
// Update the RecyclerView's padding such that the bottom insets are applied
// while still preserving bottom padding.
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
return insets
}
override fun onScrolled(dx: Int, dy: Int) {
super.onScrolled(dx, dy)
// Scroll event occurred, need to update the dividers.

View file

@ -35,7 +35,7 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMusicDirsBinding
import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.getSystemServiceCompat
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -47,7 +47,7 @@ import org.oxycblt.auxio.util.showToast
*/
@AndroidEntryPoint
class MusicDirsDialog :
ViewBindingDialogFragment<DialogMusicDirsBinding>(), DirectoryAdapter.Listener {
ViewBindingMaterialDialogFragment<DialogMusicDirsBinding>(), DirectoryAdapter.Listener {
private val dirAdapter = DirectoryAdapter(this)
private var openDocumentTreeLauncher: ActivityResultLauncher<Uri?>? = null
private var storageManager: StorageManager? = null

View file

@ -29,19 +29,19 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogSeparatorsBinding
import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.logW
/**
* A [ViewBindingDialogFragment] that allows the user to configure the separator characters used to
* split tags with multiple values.
* A [ViewBindingMaterialDialogFragment] that allows the user to configure the separator characters
* used to split tags with multiple values.
*
* @author Alexander Capehart (OxygenCobalt)
*
* TODO: Replace with unsplit names dialog
*/
@AndroidEntryPoint
class SeparatorsDialog : ViewBindingDialogFragment<DialogSeparatorsBinding>() {
class SeparatorsDialog : ViewBindingMaterialDialogFragment<DialogSeparatorsBinding>() {
@Inject lateinit var musicSettings: MusicSettings
override fun onCreateBinding(inflater: LayoutInflater) =

View file

@ -33,7 +33,7 @@ import org.oxycblt.auxio.databinding.DialogMusicChoicesBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -45,7 +45,7 @@ import org.oxycblt.auxio.util.showToast
*/
@AndroidEntryPoint
class AddToPlaylistDialog :
ViewBindingDialogFragment<DialogMusicChoicesBinding>(),
ViewBindingMaterialDialogFragment<DialogMusicChoicesBinding>(),
ClickableListListener<PlaylistChoice>,
NewPlaylistFooterAdapter.Listener {
private val musicModel: MusicViewModel by activityViewModels()

View file

@ -30,19 +30,19 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogDeletePlaylistBinding
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A [ViewBindingDialogFragment] that asks the user to confirm the deletion of a [Playlist].
* A [ViewBindingMaterialDialogFragment] that asks the user to confirm the deletion of a [Playlist].
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class DeletePlaylistDialog : ViewBindingDialogFragment<DialogDeletePlaylistBinding>() {
class DeletePlaylistDialog : ViewBindingMaterialDialogFragment<DialogDeletePlaylistBinding>() {
private val pickerModel: PlaylistPickerViewModel by viewModels()
private val musicModel: MusicViewModel by activityViewModels()
// Information about what playlist to name for is initially within the navigation arguments

View file

@ -30,7 +30,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -42,7 +42,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class NewPlaylistDialog : ViewBindingDialogFragment<DialogPlaylistNameBinding>() {
class NewPlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBinding>() {
private val musicModel: MusicViewModel by activityViewModels()
private val pickerModel: PlaylistPickerViewModel by viewModels()
// Information about what playlist to name for is initially within the navigation arguments

View file

@ -31,7 +31,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPlaylistNameBinding
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.showToast
@ -43,7 +43,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class RenamePlaylistDialog : ViewBindingDialogFragment<DialogPlaylistNameBinding>() {
class RenamePlaylistDialog : ViewBindingMaterialDialogFragment<DialogPlaylistNameBinding>() {
private val musicModel: MusicViewModel by activityViewModels()
private val pickerModel: PlaylistPickerViewModel by viewModels()
// Information about what playlist to name for is initially within the navigation arguments

View file

@ -33,19 +33,19 @@ import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A picker [ViewBindingDialogFragment] intended for when [Artist] playback is ambiguous.
* A picker [ViewBindingMaterialDialogFragment] intended for when [Artist] playback is ambiguous.
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class PlayFromArtistDialog :
ViewBindingDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Artist> {
ViewBindingMaterialDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Artist> {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val pickerModel: PlaybackPickerViewModel by viewModels()
// Information about what Song to show choices for is initially within the navigation arguments

View file

@ -33,19 +33,19 @@ import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A picker [ViewBindingDialogFragment] intended for when [Genre] playback is ambiguous.
* A picker [ViewBindingMaterialDialogFragment] intended for when [Genre] playback is ambiguous.
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class PlayFromGenreDialog :
ViewBindingDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Genre> {
ViewBindingMaterialDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Genre> {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val pickerModel: PlaybackPickerViewModel by viewModels()
// Information about what Song to show choices for is initially within the navigation arguments

View file

@ -28,16 +28,17 @@ import kotlin.math.abs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPreAmpBinding
import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.logD
/**
* aa [ViewBindingDialogFragment] that allows user configuration of the current [ReplayGainPreAmp].
* aa [ViewBindingMaterialDialogFragment] that allows user configuration of the current
* [ReplayGainPreAmp].
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class PreAmpCustomizeDialog : ViewBindingDialogFragment<DialogPreAmpBinding>() {
class PreAmpCustomizeDialog : ViewBindingMaterialDialogFragment<DialogPreAmpBinding>() {
@Inject lateinit var playbackSettings: PlaybackSettings
override fun onCreateBinding(inflater: LayoutInflater) = DialogPreAmpBinding.inflate(inflater)

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2022 Auxio Project
* ViewBindingBottomSheetDialogFragment.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.viewbinding.ViewBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A lifecycle-aware [DialogFragment] that automatically manages the [ViewBinding] lifecycle as a
* [BottomSheetDialogFragment].
*
* @author Alexander Capehart (OxygenCobalt)
*/
abstract class ViewBindingBottomSheetDialogFragment<VB : ViewBinding> :
BottomSheetDialogFragment() {
private var _binding: VB? = null
/**
* Configure the [AlertDialog.Builder] during [onCreateDialog].
*
* @param builder The [AlertDialog.Builder] to configure.
* @see onCreateDialog
*/
protected open fun onConfigDialog(builder: AlertDialog.Builder) {}
/**
* Inflate the [ViewBinding] during [onCreateView].
*
* @param inflater The [LayoutInflater] to inflate the [ViewBinding] with.
* @return A new [ViewBinding] instance.
* @see onCreateView
*/
protected abstract fun onCreateBinding(inflater: LayoutInflater): VB
/**
* Configure the newly-inflated [ViewBinding] during [onViewCreated].
*
* @param binding The [ViewBinding] to configure.
* @param savedInstanceState The previously saved state of the UI.
* @see onViewCreated
*/
protected open fun onBindingCreated(binding: VB, savedInstanceState: Bundle?) {}
/**
* Free memory held by the [ViewBinding] during [onDestroyView]
*
* @param binding The [ViewBinding] to release.
* @see onDestroyView
*/
protected open fun onDestroyBinding(binding: VB) {}
/** The [ViewBinding], or null if it has not been inflated yet. */
protected val binding: VB?
get() = _binding
/**
* Get the [ViewBinding] under the assumption that it has been inflated.
*
* @return The currently-inflated [ViewBinding].
* @throws IllegalStateException if the [ViewBinding] is not inflated.
*/
protected fun requireBinding() =
requireNotNull(_binding) {
"ViewBinding was available. Fragment should be a valid state " +
"right now, but instead it was ${lifecycle.currentState}"
}
final override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = onCreateBinding(inflater).also { _binding = it }.root
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
onBindingCreated(requireBinding(), savedInstanceState)
logD("Fragment created")
}
final override fun onDestroyView() {
super.onDestroyView()
onDestroyBinding(unlikelyToBeNull(_binding))
// Clear binding
_binding = null
logD("Fragment destroyed")
}
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2022 Auxio Project
* ViewBindingDialogFragment.kt is part of Auxio.
* ViewBindingMaterialDialogFragment.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -32,11 +32,12 @@ import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A lifecycle-aware [DialogFragment] that automatically manages the [ViewBinding] lifecycle.
* A lifecycle-aware [DialogFragment] that automatically manages the [ViewBinding] lifecycle as a
* material dialog.
*
* @author Alexander Capehart (OxygenCobalt)
*/
abstract class ViewBindingDialogFragment<VB : ViewBinding> : DialogFragment() {
abstract class ViewBindingMaterialDialogFragment<VB : ViewBinding> : DialogFragment() {
private var _binding: VB? = null
/**

View file

@ -29,18 +29,18 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogAccentBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A [ViewBindingDialogFragment] that allows the user to configure the current [Accent].
* A [ViewBindingMaterialDialogFragment] that allows the user to configure the current [Accent].
*
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class AccentCustomizeDialog :
ViewBindingDialogFragment<DialogAccentBinding>(), ClickableListListener<Accent> {
ViewBindingMaterialDialogFragment<DialogAccentBinding>(), ClickableListListener<Accent> {
private var accentAdapter = AccentAdapter(this)
@Inject lateinit var uiSettings: UISettings

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Auxio.RecyclerView.Linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/spacing_medium"
android:paddingHorizontal="@dimen/spacing_medium"
android:textAppearance="@style/TextAppearance.Auxio.HeadlineSmall"
android:text="TODO: Put info here" />
<org.oxycblt.auxio.list.recycler.DialogRecyclerView
android:id="@+id/menu_recycler"
style="@style/Widget.Auxio.RecyclerView.Linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_menu_option" />
</LinearLayout>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/title"
android:textAppearance="@style/TextAppearance.Auxio.TitleMedium"
android:padding="@dimen/spacing_medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="@drawable/ui_item_ripple"
tools:text="Songs" />

View file

@ -30,6 +30,9 @@
<action
android:id="@+id/show_genre"
app:destination="@id/genre_detail_fragment" />
<action
android:id="@+id/open_menu"
app:destination="@id/menu_dialog" />
<action
android:id="@+id/show_playlist"
app:destination="@id/playlist_detail_fragment" />
@ -297,6 +300,12 @@
app:argType="org.oxycblt.auxio.music.Music$UID" />
</dialog>
<dialog
android:id="@+id/menu_dialog"
android:name="org.oxycblt.auxio.list.menu.MenuDialogFragment"
android:label="menu_dialog"
tools:layout="@layout/dialog_menu" />
<fragment
android:id="@+id/root_preferences_fragment"
android:name="org.oxycblt.auxio.settings.RootPreferenceFragment"

View file

@ -23,6 +23,8 @@
<item name="materialAlertDialogTheme">@style/Theme.Auxio.Dialog</item>
<item name="sliderStyle">@style/Widget.Auxio.Slider</item>
<item name="linearProgressIndicatorStyle">@style/Widget.Auxio.LinearProgressIndicator</item>
<item name="bottomSheetStyle">@style/Widget.Auxio.BottomSheet</item>
<item name="bottomSheetDialogTheme">@style/Widget.Auxio.BottomSheet.Dialog</item>
<item name="textAppearanceDisplayLarge">@style/TextAppearance.Auxio.DisplayLarge</item>
<item name="textAppearanceDisplayMedium">@style/TextAppearance.Auxio.DisplayMedium</item>

View file

@ -31,6 +31,28 @@
<item name="trackCornerRadius">@dimen/size_corners_medium</item>
</style>
<style name="Widget.Auxio.BottomSheet" parent="Widget.Material3.BottomSheet">
<item name="marginLeftSystemWindowInsets">false</item>
<item name="marginRightSystemWindowInsets">false</item>
<item name="paddingBottomSystemWindowInsets">false</item>
<item name="paddingTopSystemWindowInsets">false</item>
<item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.None</item>
<item name="shouldRemoveExpandedCorners">true</item>
</style>
<style name="Widget.Auxio.BottomSheet.Modal" parent="Widget.Material3.BottomSheet.Modal">
<item name="marginLeftSystemWindowInsets">false</item>
<item name="marginRightSystemWindowInsets">false</item>
<item name="paddingBottomSystemWindowInsets">false</item>
<item name="paddingTopSystemWindowInsets">false</item>
<item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.None</item>
<item name="shouldRemoveExpandedCorners">true</item>
</style>
<style name="Widget.Auxio.BottomSheet.Dialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="bottomSheetStyle">@style/Widget.Auxio.BottomSheet.Modal</item>
</style>
<style name="Widget.Auxio.Image.Small" parent="">
<item name="android:layout_width">@dimen/size_cover_compact</item>
<item name="android:layout_height">@dimen/size_cover_compact</item>