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:
parent
2aa540c29a
commit
717f49fc20
37 changed files with 451 additions and 106 deletions
|
@ -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])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
9
app/src/main/res/layout/dialog_music_picker.xml
Normal file
9
app/src/main/res/layout/dialog_music_picker.xml
Normal 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" />
|
35
app/src/main/res/layout/item_picker_choice.xml
Normal file
35
app/src/main/res/layout/item_picker_choice.xml
Normal 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>
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue