music: add picker dialog

Add a dialog for picking a genre from several choices.

This basically completes multi-genre support in Auxio, save more
internal reworks.

Note that it is extremely likely that the "Play from genre" setting
will be removed soon. This feature has made me realize that such does
not many any real sense, as genres are more semantically similar to
playlists than artists or albums. This implementation only exists to
make multi-artist support an easy plug-and-play operation.

Resolves #201
This commit is contained in:
Alexander Capehart 2022-09-14 19:48:39 -06:00
parent 2aa540c29a
commit 717f49fc20
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
37 changed files with 451 additions and 106 deletions

View file

@ -29,16 +29,20 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.LinearSmoothScroller
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.AlbumDetailAdapter import org.oxycblt.auxio.detail.recycler.AlbumDetailAdapter
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.canScroll import org.oxycblt.auxio.util.canScroll
@ -125,11 +129,19 @@ class AlbumDetailFragment :
override fun onItemClick(item: Item) { override fun onItemClick(item: Item) {
check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" } check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" }
val playbackMode = settings.detailPlaybackMode when (settings.detailPlaybackMode) {
if (playbackMode != null) { null, MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
playbackModel.play(item, playbackMode) MusicMode.SONGS -> playbackModel.play(item)
} else { MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
playbackModel.playFromAlbum(item) MusicMode.GENRES -> if (item.genres.size > 1) {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.showGenrePickerDialog(item.uid, PickerMode.PLAY)
)
)
} else {
playbackModel.playFromGenre(item, item.genres[0])
}
} }
} }

View file

@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter import org.oxycblt.auxio.detail.recycler.ArtistDetailAdapter
@ -33,10 +34,13 @@ import org.oxycblt.auxio.detail.recycler.DetailAdapter
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
@ -117,11 +121,19 @@ class ArtistDetailFragment :
override fun onItemClick(item: Item) { override fun onItemClick(item: Item) {
when (item) { when (item) {
is Song -> { is Song -> {
val playbackMode = settings.detailPlaybackMode when (settings.detailPlaybackMode) {
if (playbackMode != null) { null, MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
playbackModel.play(item, playbackMode) MusicMode.SONGS -> playbackModel.play(item)
} else { MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
playbackModel.playFromArtist(item) MusicMode.GENRES -> if (item.genres.size > 1) {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.showGenrePickerDialog(item.uid, PickerMode.PLAY)
)
)
} else {
playbackModel.playFromGenre(item, item.genres[0])
}
} }
} }
is Album -> navModel.exploreNavigateTo(item) is Album -> navModel.exploreNavigateTo(item)

View file

@ -38,7 +38,7 @@ import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.ReleaseType import org.oxycblt.auxio.music.ReleaseType
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item

View file

@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.recycler.DetailAdapter import org.oxycblt.auxio.detail.recycler.DetailAdapter
@ -34,10 +35,13 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
@ -117,11 +121,20 @@ class GenreDetailFragment :
override fun onItemClick(item: Item) { override fun onItemClick(item: Item) {
check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" } check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" }
val playbackMode = settings.detailPlaybackMode when (settings.detailPlaybackMode) {
if (playbackMode != null) { null -> playbackModel.playFromGenre(item, unlikelyToBeNull(detailModel.currentGenre.value))
playbackModel.play(item, playbackMode) MusicMode.SONGS -> playbackModel.play(item)
} else { MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
playbackModel.playFromGenre(item, unlikelyToBeNull(detailModel.currentGenre.value)) MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> if (item.genres.size > 1) {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.showGenrePickerDialog(item.uid, PickerMode.PLAY)
)
)
} else {
playbackModel.playFromGenre(item, item.genres[0])
}
} }
} }

View file

@ -21,7 +21,7 @@ import android.content.Context
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
/** /**

View file

@ -48,11 +48,11 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.system.Indexer import org.oxycblt.auxio.music.system.Indexer
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.NavigationViewModel

View file

@ -26,10 +26,10 @@ import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.util.application import org.oxycblt.auxio.util.application
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD

View file

@ -24,11 +24,11 @@ import android.view.ViewGroup
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.music.formatDurationMs
import org.oxycblt.auxio.music.secsToMs import org.oxycblt.auxio.music.secsToMs
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item

View file

@ -23,10 +23,10 @@ import android.view.ViewGroup
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.music.formatDurationMs
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item

View file

@ -23,10 +23,10 @@ import android.view.ViewGroup
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.music.formatDurationMs
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.ui.recycler.GenreViewHolder import org.oxycblt.auxio.ui.recycler.GenreViewHolder
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item

View file

@ -21,15 +21,18 @@ import android.os.Bundle
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentHomeListBinding import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.music.formatDurationMs
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.music.secsToMs import org.oxycblt.auxio.music.secsToMs
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.MenuItemListener
@ -109,7 +112,20 @@ class SongListFragment : HomeListFragment<Song>() {
override fun onItemClick(item: Item) { override fun onItemClick(item: Item) {
check(item is Song) { "Unexpected datatype: ${item::class.java}" } check(item is Song) { "Unexpected datatype: ${item::class.java}" }
playbackModel.play(item, settings.libPlaybackMode) when (settings.libPlaybackMode) {
MusicMode.SONGS -> playbackModel.play(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> if (item.genres.size > 1) {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.showGenrePickerDialog(item.uid, PickerMode.PLAY)
)
)
} else {
playbackModel.playFromGenre(item, item.genres[0])
}
}
} }
override fun onOpenMenu(item: Item, anchor: View) { override fun onOpenMenu(item: Item, anchor: View) {

View file

@ -21,7 +21,7 @@ import org.oxycblt.auxio.home.tabs.Tab.Companion.fromSequence
import org.oxycblt.auxio.home.tabs.Tab.Companion.toSequence import org.oxycblt.auxio.home.tabs.Tab.Companion.toSequence
import org.oxycblt.auxio.home.tabs.Tab.Invisible import org.oxycblt.auxio.home.tabs.Tab.Invisible
import org.oxycblt.auxio.home.tabs.Tab.Visible import org.oxycblt.auxio.home.tabs.Tab.Visible
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.util.logE import org.oxycblt.auxio.util.logE
/** /**

View file

@ -24,7 +24,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.ItemTabBinding import org.oxycblt.auxio.databinding.ItemTabBinding
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.ui.recycler.DialogViewHolder import org.oxycblt.auxio.ui.recycler.DialogViewHolder
import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.inflater

View file

@ -25,7 +25,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogTabsBinding import org.oxycblt.auxio.databinding.DialogTabsBinding
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
import org.oxycblt.auxio.util.context import org.oxycblt.auxio.util.context

View file

@ -34,7 +34,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.Sort
import kotlin.math.min import kotlin.math.min
/** A basic keyer for music data. */ /** A basic keyer for music data. */

View file

@ -26,8 +26,6 @@ import kotlinx.parcelize.Parcelize
import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Date.Companion.from import org.oxycblt.auxio.music.Date.Companion.from
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.inRangeOrNull import org.oxycblt.auxio.util.inRangeOrNull
import org.oxycblt.auxio.util.nonZeroOrNull import org.oxycblt.auxio.util.nonZeroOrNull
@ -105,43 +103,34 @@ sealed class Music : Item {
*/ */
@Parcelize @Parcelize
class UID private constructor( class UID private constructor(
private val isMusicBrainz: Boolean, private val format: Format,
private val mode: MusicMode, private val mode: MusicMode,
private val uuid: UUID private val uuid: UUID
) : Parcelable { ) : Parcelable {
// Cache the hashCode for speed // Cache the hashCode for speed
@IgnoredOnParcel private val hashCode: Int @IgnoredOnParcel private var hashCode = format.hashCode()
init { init {
var result = isMusicBrainz.hashCode() hashCode = 31 * hashCode + mode.hashCode()
result = 31 * result + mode.hashCode() hashCode = 31 * hashCode + uuid.hashCode()
result = 31 * result + uuid.hashCode()
hashCode = result
} }
override fun hashCode() = hashCode override fun hashCode() = hashCode
override fun equals(other: Any?) = other is UID && override fun equals(other: Any?) = other is UID &&
isMusicBrainz == other.isMusicBrainz && format == other.format &&
mode == other.mode && uuid == other.uuid mode == other.mode && uuid == other.uuid
override fun toString(): String { // UID string format is roughly:
// Format comes first, delimited by a ":". // format_namespace:music_mode_int-uuid
val format = if (isMusicBrainz) { override fun toString() = "${format.namespace}:${mode.intCode.toString(16)}-$uuid"
FORMAT_MUSICBRAINZ
} else {
FORMAT_AUXIO
}
// Instead of making new string values for the mode, be lazy and just append it's private enum class Format(val namespace: String) {
// intCode in front of the UUID. AUXIO("org.oxycblt.auxio"),
return "$format:${mode.intCode.toString(16)}-$uuid" MUSICBRAINZ("org.musicbrainz")
} }
companion object { companion object {
private const val FORMAT_AUXIO = "org.oxycblt.auxio"
private const val FORMAT_MUSICBRAINZ = "org.musicbrainz"
/** Parse a [UID] from the string [uid]. Returns null if not valid. */ /** Parse a [UID] from the string [uid]. Returns null if not valid. */
fun fromString(uid: String): UID? { fun fromString(uid: String): UID? {
val split = uid.split(':', limit = 2) val split = uid.split(':', limit = 2)
@ -149,9 +138,9 @@ sealed class Music : Item {
return null return null
} }
val isMusicBrainz = when (split[0]) { val format = when (split[0]) {
FORMAT_MUSICBRAINZ -> true Format.AUXIO.namespace -> Format.AUXIO
FORMAT_AUXIO -> false Format.MUSICBRAINZ.namespace -> Format.MUSICBRAINZ
else -> return null else -> return null
} }
@ -163,7 +152,7 @@ sealed class Music : Item {
val mode = MusicMode.fromInt(ids[0].toIntOrNull(16) ?: return null) ?: return null val mode = MusicMode.fromInt(ids[0].toIntOrNull(16) ?: return null) ?: return null
val uuid = UUID.fromString(ids[1]) val uuid = UUID.fromString(ids[1])
return UID(isMusicBrainz, mode, uuid) return UID(format, mode, uuid)
} }
/** /**
@ -178,7 +167,7 @@ sealed class Music : Item {
val digest = MessageDigest.getInstance("MD5") val digest = MessageDigest.getInstance("MD5")
updates(digest) updates(digest)
val uuid = digest.digest().toUuid() val uuid = digest.digest().toUuid()
return UID(false, mode, uuid) return UID(Format.AUXIO, mode, uuid)
} }
} }
} }

View file

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.ui package org.oxycblt.auxio.music
import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.IntegerTable

View file

@ -26,7 +26,6 @@ import android.provider.MediaStore
import android.text.format.DateUtils import android.text.format.DateUtils
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import java.util.UUID
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */ /** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
fun ContentResolver.queryCursor( fun ContentResolver.queryCursor(

View file

@ -15,18 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.ui package org.oxycblt.auxio.music
import androidx.annotation.IdRes import androidx.annotation.IdRes
import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Sort.Mode
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Date
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.Sort.Mode
/** /**
* Represents the sort modes used in Auxio. * Represents the sort modes used in Auxio.

View file

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.settings package org.oxycblt.auxio.music.dirs
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup

View file

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.settings package org.oxycblt.auxio.music.dirs
import org.oxycblt.auxio.music.Directory import org.oxycblt.auxio.music.Directory

View file

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.settings package org.oxycblt.auxio.music.dirs
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2022 Auxio Project
*
* 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.music.picker
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.ItemPickerChoiceBinding
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.ui.recycler.DialogViewHolder
import org.oxycblt.auxio.ui.recycler.ItemClickListener
import org.oxycblt.auxio.util.context
import org.oxycblt.auxio.util.inflater
/**
* The adapter that displays a list of genre choices in the picker UI.
*/
class GenreChoiceAdapter(private val listener: ItemClickListener) : RecyclerView.Adapter<GenreChoiceViewHolder>() {
private var genres = listOf<Genre>()
override fun getItemCount() = genres.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
GenreChoiceViewHolder.new(parent)
override fun onBindViewHolder(holder: GenreChoiceViewHolder, position: Int) =
holder.bind(genres[position], listener)
fun submitList(newGenres: List<Genre>) {
if (newGenres != genres) {
genres = newGenres
@Suppress("NotifyDataSetChanged")
notifyDataSetChanged()
}
}
}
/**
* The ViewHolder that displays a genre choice. Smaller than other parent items due to dialog
* constraints.
*/
class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) : DialogViewHolder(binding.root) {
fun bind(genre: Genre, listener: ItemClickListener) {
binding.pickerImage.bind(genre)
binding.pickerName.text = genre.resolveName(binding.context)
binding.root.setOnClickListener {
listener.onItemClick(genre)
}
}
companion object {
fun new(parent: View) =
GenreChoiceViewHolder(ItemPickerChoiceBinding.inflate(parent.context.inflater))
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2022 Auxio Project
*
* 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.music.picker
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.NavigationViewModel
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.ItemClickListener
import org.oxycblt.auxio.util.androidActivityViewModels
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A dialog that shows several genre options if the result of an genre-reliant operation is
* ambiguous.
* @author OxygenCobalt
*/
class GenrePickerDialog : ViewBindingDialogFragment<DialogMusicPickerBinding>(), ItemClickListener {
private val pickerModel: PickerViewModel by viewModels()
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
private val navModel: NavigationViewModel by activityViewModels()
private val args: GenrePickerDialogArgs by navArgs()
private val adapter = GenreChoiceAdapter(this)
override fun onCreateBinding(inflater: LayoutInflater) =
DialogMusicPickerBinding.inflate(inflater)
override fun onConfigDialog(builder: AlertDialog.Builder) {
builder
.setTitle(
when (args.pickerMode) {
PickerMode.GO -> R.string.lbl_go_genre
PickerMode.PLAY -> R.string.lbl_play_genre
}
)
.setNegativeButton(R.string.lbl_cancel, null)
}
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
pickerModel.setSongUid(args.songUid)
binding.pickerRecycler.adapter = adapter
collectImmediately(pickerModel.currentSong) { song ->
if (song != null) {
adapter.submitList(song.genres)
} else {
findNavController().navigateUp()
}
}
}
override fun onDestroyBinding(binding: DialogMusicPickerBinding) {
binding.pickerRecycler.adapter = null
}
override fun onItemClick(item: Item) {
check(item is Genre) { "Unexpected datatype: ${item::class.simpleName}" }
findNavController().navigateUp()
when (args.pickerMode) {
PickerMode.GO -> navModel.exploreNavigateTo(item)
PickerMode.PLAY -> {
val song = unlikelyToBeNull(pickerModel.currentSong.value)
playbackModel.playFromGenre(song, item)
}
}
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 Auxio Project
*
* 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.music.picker
/**
* Represents the actions available to the picker UI.
*/
enum class PickerMode {
PLAY,
GO
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2022 Auxio Project
*
* 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.music.picker
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.unlikelyToBeNull
/**
* A small ViewModel holding and updating the current song being shown in the picker UI.
* @author OxygenCobalt
*/
class PickerViewModel : ViewModel(), MusicStore.Callback {
private val musicStore = MusicStore.getInstance()
private val _currentSong = MutableStateFlow<Song?>(null)
val currentSong: StateFlow<Song?> get() = _currentSong
fun setSongUid(uid: Music.UID) {
if (_currentSong.value?.uid == uid) return
val library = unlikelyToBeNull(musicStore.library)
_currentSong.value = requireNotNull(library.find(uid)) { "Invalid song id provided" }
}
override fun onLibraryChanged(library: MusicStore.Library?) {
if (library != null) {
val song = _currentSong.value
if (song != null) {
_currentSong.value = library.sanitize(song)
}
}
}
}

View file

@ -15,14 +15,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.oxycblt.auxio.music.settings package org.oxycblt.auxio.music.separators
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.children import androidx.core.view.children
import com.google.android.material.checkbox.MaterialCheckBox import com.google.android.material.checkbox.MaterialCheckBox
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogSeparatorsBinding import org.oxycblt.auxio.databinding.DialogSeparatorsBinding
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings

View file

@ -32,12 +32,12 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.extractor.Api21MediaStoreLayer import org.oxycblt.auxio.music.extractor.Api21MediaStoreLayer
import org.oxycblt.auxio.music.extractor.Api29MediaStoreLayer import org.oxycblt.auxio.music.extractor.Api29MediaStoreLayer
import org.oxycblt.auxio.music.extractor.Api30MediaStoreLayer import org.oxycblt.auxio.music.extractor.Api30MediaStoreLayer
import org.oxycblt.auxio.music.extractor.CacheLayer import org.oxycblt.auxio.music.extractor.CacheLayer
import org.oxycblt.auxio.music.extractor.MetadataLayer import org.oxycblt.auxio.music.extractor.MetadataLayer
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE import org.oxycblt.auxio.util.logE
import org.oxycblt.auxio.util.logW import org.oxycblt.auxio.util.logW

View file

@ -32,7 +32,6 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.dsToMs import org.oxycblt.auxio.music.dsToMs
import org.oxycblt.auxio.music.msToDs import org.oxycblt.auxio.music.msToDs
import org.oxycblt.auxio.music.ui.MusicMode
import org.oxycblt.auxio.playback.state.InternalPlayer import org.oxycblt.auxio.playback.state.InternalPlayer
import org.oxycblt.auxio.playback.state.PlaybackStateDatabase import org.oxycblt.auxio.playback.state.PlaybackStateDatabase
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
@ -92,19 +91,9 @@ class PlaybackViewModel(application: Application) :
// --- PLAYING FUNCTIONS --- // --- PLAYING FUNCTIONS ---
/** Play a [song] with the [mode] specified, */ /** Play a [song] from all songs. */
fun play(song: Song, mode: MusicMode) { fun play(song: Song) {
// TODO: Remove this function when selection is implemented playbackManager.play(song, null, settings)
val parent =
when (mode) {
MusicMode.GENRES -> song.album
MusicMode.ARTISTS -> song.album.artist
MusicMode.ALBUMS -> song.genres.maxBy { it.songs.size }
MusicMode.SONGS -> null
}
playbackManager.play(song, parent, settings)
} }
/** Play a song from it's album. */ /** Play a song from it's album. */

View file

@ -21,7 +21,6 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogPreAmpBinding import org.oxycblt.auxio.databinding.DialogPreAmpBinding
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings

View file

@ -29,16 +29,19 @@ import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSearchBinding import org.oxycblt.auxio.databinding.FragmentSearchBinding
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.MenuItemListener
@ -147,7 +150,20 @@ class SearchFragment :
override fun onItemClick(item: Item) { override fun onItemClick(item: Item) {
when (item) { when (item) {
is Song -> playbackModel.play(item, settings.libPlaybackMode) is Song -> when (settings.libPlaybackMode) {
MusicMode.SONGS -> playbackModel.play(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> playbackModel.playFromArtist(item)
MusicMode.GENRES -> if (item.genres.size > 1) {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.showGenrePickerDialog(item.uid, PickerMode.PLAY)
)
)
} else {
playbackModel.playFromGenre(item, item.genres[0])
}
}
is MusicParent -> navModel.exploreNavigateTo(item) is MusicParent -> navModel.exploreNavigateTo(item)
} }
} }

View file

@ -33,10 +33,10 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.ui.Sort
import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Header
import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.Item

View file

@ -28,9 +28,9 @@ import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.music.Directory import org.oxycblt.auxio.music.Directory
import org.oxycblt.auxio.music.settings.MusicDirs import org.oxycblt.auxio.music.MusicMode
import org.oxycblt.auxio.music.ui.MusicMode import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.ui.Sort import org.oxycblt.auxio.music.dirs.MusicDirs
import org.oxycblt.auxio.playback.BarAction import org.oxycblt.auxio.playback.BarAction
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<org.oxycblt.auxio.ui.recycler.DialogRecyclerView 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:id="@+id/picker_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_picker_choice" />

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/spacing_mid_large"
android:paddingTop="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_mid_large"
android:paddingBottom="@dimen/spacing_medium">
<org.oxycblt.auxio.image.ImageGroup
android:id="@+id/picker_image"
style="@style/Widget.Auxio.Image.Small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:staticIcon="@drawable/ic_song_24" />
<TextView
android:id="@+id/picker_name"
style="@style/Widget.Auxio.TextView.Item.Primary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_mid_medium"
android:textColor="@color/sel_accented_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/picker_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Artist" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -17,8 +17,24 @@
<action <action
android:id="@+id/action_show_details" android:id="@+id/action_show_details"
app:destination="@id/song_detail_dialog" /> app:destination="@id/song_detail_dialog" />
<action
android:id="@+id/show_genre_picker_dialog"
app:destination="@id/genre_picker_dialog" />
</fragment> </fragment>
<dialog
android:id="@+id/genre_picker_dialog"
android:name="org.oxycblt.auxio.music.picker.GenrePickerDialog"
android:label="genre_picker_dialog"
tools:layout="@layout/dialog_music_picker">
<argument
android:name="songUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
<argument
android:name="pickerMode"
app:argType="org.oxycblt.auxio.music.picker.PickerMode" />
</dialog>
<dialog <dialog
android:id="@+id/song_detail_dialog" android:id="@+id/song_detail_dialog"
android:name="org.oxycblt.auxio.detail.SongDetailDialog" android:name="org.oxycblt.auxio.detail.SongDetailDialog"
@ -67,12 +83,12 @@
tools:layout="@layout/dialog_pre_amp" /> tools:layout="@layout/dialog_pre_amp" />
<dialog <dialog
android:id="@+id/music_dirs_dialog" android:id="@+id/music_dirs_dialog"
android:name="org.oxycblt.auxio.music.settings.MusicDirsDialog" android:name="org.oxycblt.auxio.music.dirs.MusicDirsDialog"
android:label="music_dirs_dialog" android:label="music_dirs_dialog"
tools:layout="@layout/dialog_music_dirs" /> tools:layout="@layout/dialog_music_dirs" />
<dialog <dialog
android:id="@+id/separators_dialog" android:id="@+id/separators_dialog"
android:name="org.oxycblt.auxio.music.settings.SeparatorsDialog" android:name="org.oxycblt.auxio.music.separators.SeparatorsDialog"
android:label="music_dirs_dialog" android:label="music_dirs_dialog"
tools:layout="@layout/dialog_separators" /> tools:layout="@layout/dialog_separators" />

View file

@ -102,8 +102,11 @@
<string name="lbl_play_next">Play next</string> <string name="lbl_play_next">Play next</string>
<string name="lbl_queue_add">Add to queue</string> <string name="lbl_queue_add">Add to queue</string>
<string name="lbl_go_genre">Go to genre</string>
<string name="lbl_go_artist">Go to artist</string> <string name="lbl_go_artist">Go to artist</string>
<string name="lbl_go_album">Go to album</string> <string name="lbl_go_album">Go to album</string>
<string name="lbl_play_genre">Play from genre</string>
<string name="lbl_play_artist">Play from artist</string>
<string name="lbl_song_detail">View properties</string> <string name="lbl_song_detail">View properties</string>
<string name="lbl_props">Song properties</string> <string name="lbl_props">Song properties</string>
@ -203,8 +206,8 @@
<string name="set_playback_mode_none">Play from shown item</string> <string name="set_playback_mode_none">Play from shown item</string>
<string name="set_playback_mode_all">Play from all songs</string> <string name="set_playback_mode_all">Play from all songs</string>
<string name="set_playback_mode_album">Play from album</string> <string name="set_playback_mode_album">Play from album</string>
<string name="set_playback_mode_artist">Play from artist</string> <string name="set_playback_mode_artist">@string/lbl_play_artist</string>
<string name="set_playback_mode_genre">Play from genre</string> <string name="set_playback_mode_genre">@string/lbl_play_genre</string>
<string name="set_keep_shuffle">Remember shuffle</string> <string name="set_keep_shuffle">Remember shuffle</string>
<string name="set_keep_shuffle_desc">Keep shuffle on when playing a new song</string> <string name="set_keep_shuffle_desc">Keep shuffle on when playing a new song</string>
<string name="set_rewind_prev">Rewind before skipping back</string> <string name="set_rewind_prev">Rewind before skipping back</string>