all: merge display and playback modes
Merge DisplayMode and PlaybackMode into a new class called MusicMode. Both of these datatypes represented similar things, and thus it's much easier to make them the same datatype. Moreover, it makes the forthcoming addition of the music selector much easier if the same datatype was tied to the representation of music. This commit also moves around things around the project to be slightly more coherent.
This commit is contained in:
parent
8aac87e02c
commit
c342fb364b
52 changed files with 322 additions and 355 deletions
|
@ -25,16 +25,19 @@ import androidx.core.graphics.drawable.IconCompat
|
|||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.request.CachePolicy
|
||||
import org.oxycblt.auxio.image.AlbumCoverFetcher
|
||||
import org.oxycblt.auxio.image.ArtistImageFetcher
|
||||
import org.oxycblt.auxio.image.CrossfadeTransitionFactory
|
||||
import org.oxycblt.auxio.image.GenreImageFetcher
|
||||
import org.oxycblt.auxio.image.MusicKeyer
|
||||
import org.oxycblt.auxio.image.extractor.AlbumCoverFetcher
|
||||
import org.oxycblt.auxio.image.extractor.ArtistImageFetcher
|
||||
import org.oxycblt.auxio.image.extractor.CrossfadeTransitionFactory
|
||||
import org.oxycblt.auxio.image.extractor.GenreImageFetcher
|
||||
import org.oxycblt.auxio.image.extractor.MusicKeyer
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
|
||||
class AuxioApp : Application(), ImageLoaderFactory {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Settings(this).migrate()
|
||||
|
||||
// Adding static shortcuts in a dynamic manner is better than declaring them
|
||||
// manually, as it will properly handle the difference between debug and release
|
||||
// Auxio instances.
|
||||
|
|
|
@ -90,17 +90,17 @@ object IntegerTable {
|
|||
|
||||
/** DisplayMode.NONE (No Longer used but still reserved) */
|
||||
// const val DISPLAY_MODE_NONE = 0xA107
|
||||
/** DisplayMode.SHOW_GENRES */
|
||||
const val DISPLAY_MODE_SHOW_GENRES = 0xA108
|
||||
/** MusicMode._GENRES */
|
||||
const val MUSIC_MODE_GENRES = 0xA108
|
||||
|
||||
/** DisplayMode.SHOW_ARTISTS */
|
||||
const val DISPLAY_MODE_SHOW_ARTISTS = 0xA109
|
||||
/** MusicMode._ARTISTS */
|
||||
const val MUSIC_MODE_ARTISTS = 0xA109
|
||||
|
||||
/** DisplayMode.SHOW_ALBUMS */
|
||||
const val DISPLAY_MODE_SHOW_ALBUMS = 0xA10A
|
||||
/** MusicMode._ALBUMS */
|
||||
const val MUSIC_MODE_ALBUMS = 0xA10A
|
||||
|
||||
/** DisplayMode.SHOW_SONGS */
|
||||
const val DISPLAY_MODE_SHOW_SONGS = 0xA10B
|
||||
/** MusicMode._SONGS */
|
||||
const val MUSIC_MODE_SONGS = 0xA10B
|
||||
|
||||
// Note: Sort integer codes are non-contiguous due to significant amounts of time
|
||||
// passing between the additions of new sort modes.
|
||||
|
|
|
@ -45,8 +45,6 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
*
|
||||
* TODO: Add multi-select
|
||||
*
|
||||
* TODO: Remove asterisk imports
|
||||
*
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
|
|
@ -34,9 +34,9 @@ import com.google.android.material.transition.MaterialFadeThrough
|
|||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackSheetBehavior
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.queue.QueueSheetBehavior
|
||||
import org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
|
|
|
@ -37,8 +37,8 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.canScroll
|
||||
|
|
|
@ -35,8 +35,8 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.collect
|
||||
|
|
|
@ -38,8 +38,8 @@ import org.oxycblt.auxio.music.Music
|
|||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.ReleaseType
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.application
|
||||
|
|
|
@ -36,8 +36,8 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.collect
|
||||
|
|
|
@ -47,14 +47,14 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.system.Indexer
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collect
|
||||
|
@ -198,7 +198,7 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
item.isChecked = !item.isChecked
|
||||
homeModel.updateCurrentSort(
|
||||
homeModel
|
||||
.getSortForDisplay(homeModel.currentTab.value)
|
||||
.getSortForTab(homeModel.currentTab.value)
|
||||
.withAscending(item.isChecked)
|
||||
)
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
item.isChecked = true
|
||||
homeModel.updateCurrentSort(
|
||||
homeModel
|
||||
.getSortForDisplay(homeModel.currentTab.value)
|
||||
.getSortForTab(homeModel.currentTab.value)
|
||||
.withMode(requireNotNull(Sort.Mode.fromItemId(item.itemId)))
|
||||
)
|
||||
}
|
||||
|
@ -216,20 +216,20 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
return true
|
||||
}
|
||||
|
||||
private fun updateCurrentTab(tab: DisplayMode) {
|
||||
private fun updateCurrentTab(tab: MusicMode) {
|
||||
// Make sure that we update the scrolling view and allowed menu items whenever
|
||||
// the tab changes.
|
||||
val binding = requireBinding()
|
||||
when (tab) {
|
||||
DisplayMode.SHOW_SONGS -> {
|
||||
MusicMode.SONGS -> {
|
||||
updateSortMenu(tab) { id -> id != R.id.option_sort_count }
|
||||
binding.homeAppbar.liftOnScrollTargetViewId = R.id.home_song_list
|
||||
}
|
||||
DisplayMode.SHOW_ALBUMS -> {
|
||||
MusicMode.ALBUMS -> {
|
||||
updateSortMenu(tab) { id -> id != R.id.option_sort_album }
|
||||
binding.homeAppbar.liftOnScrollTargetViewId = R.id.home_album_list
|
||||
}
|
||||
DisplayMode.SHOW_ARTISTS -> {
|
||||
MusicMode.ARTISTS -> {
|
||||
updateSortMenu(tab) { id ->
|
||||
id == R.id.option_sort_asc ||
|
||||
id == R.id.option_sort_name ||
|
||||
|
@ -238,7 +238,7 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
}
|
||||
binding.homeAppbar.liftOnScrollTargetViewId = R.id.home_artist_list
|
||||
}
|
||||
DisplayMode.SHOW_GENRES -> {
|
||||
MusicMode.GENRES -> {
|
||||
updateSortMenu(tab) { id ->
|
||||
id == R.id.option_sort_asc ||
|
||||
id == R.id.option_sort_name ||
|
||||
|
@ -250,9 +250,9 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateSortMenu(displayMode: DisplayMode, isVisible: (Int) -> Boolean) {
|
||||
private fun updateSortMenu(mode: MusicMode, isVisible: (Int) -> Boolean) {
|
||||
val sortMenu = requireNotNull(sortItem.subMenu)
|
||||
val toHighlight = homeModel.getSortForDisplay(displayMode)
|
||||
val toHighlight = homeModel.getSortForTab(mode)
|
||||
|
||||
for (option in sortMenu) {
|
||||
if (option.itemId == toHighlight.mode.itemId) {
|
||||
|
@ -434,10 +434,10 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (homeModel.tabs[position]) {
|
||||
DisplayMode.SHOW_SONGS -> SongListFragment()
|
||||
DisplayMode.SHOW_ALBUMS -> AlbumListFragment()
|
||||
DisplayMode.SHOW_ARTISTS -> ArtistListFragment()
|
||||
DisplayMode.SHOW_GENRES -> GenreListFragment()
|
||||
MusicMode.SONGS -> SongListFragment()
|
||||
MusicMode.ALBUMS -> AlbumListFragment()
|
||||
MusicMode.ARTISTS -> ArtistListFragment()
|
||||
MusicMode.GENRES -> GenreListFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,11 @@ import org.oxycblt.auxio.home.tabs.Tab
|
|||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.util.application
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
|
@ -59,15 +59,15 @@ class HomeViewModel(application: Application) :
|
|||
val genres: StateFlow<List<Genre>>
|
||||
get() = _genres
|
||||
|
||||
var tabs: List<DisplayMode> = visibleTabs
|
||||
var tabs: List<MusicMode> = visibleTabs
|
||||
private set
|
||||
|
||||
/** Internal getter for getting the visible library tabs */
|
||||
private val visibleTabs: List<DisplayMode>
|
||||
private val visibleTabs: List<MusicMode>
|
||||
get() = settings.libTabs.filterIsInstance<Tab.Visible>().map { it.mode }
|
||||
|
||||
private val _currentTab = MutableStateFlow(tabs[0])
|
||||
val currentTab: StateFlow<DisplayMode> = _currentTab
|
||||
val currentTab: StateFlow<MusicMode> = _currentTab
|
||||
|
||||
/**
|
||||
* Marker to recreate all library tabs, usually initiated by a settings change. When this flag
|
||||
|
@ -93,32 +93,32 @@ class HomeViewModel(application: Application) :
|
|||
_shouldRecreateTabs.value = false
|
||||
}
|
||||
|
||||
/** Get the specific sort for the given [DisplayMode]. */
|
||||
fun getSortForDisplay(displayMode: DisplayMode) =
|
||||
when (displayMode) {
|
||||
DisplayMode.SHOW_SONGS -> settings.libSongSort
|
||||
DisplayMode.SHOW_ALBUMS -> settings.libAlbumSort
|
||||
DisplayMode.SHOW_ARTISTS -> settings.libArtistSort
|
||||
DisplayMode.SHOW_GENRES -> settings.libGenreSort
|
||||
/** Get the specific sort for the given [MusicMode]. */
|
||||
fun getSortForTab(tabMode: MusicMode): Sort =
|
||||
when (tabMode) {
|
||||
MusicMode.SONGS -> settings.libSongSort
|
||||
MusicMode.ALBUMS -> settings.libAlbumSort
|
||||
MusicMode.ARTISTS -> settings.libArtistSort
|
||||
MusicMode.GENRES -> settings.libGenreSort
|
||||
}
|
||||
|
||||
/** Update the currently displayed item's [Sort]. */
|
||||
fun updateCurrentSort(sort: Sort) {
|
||||
logD("Updating ${_currentTab.value} sort to $sort")
|
||||
when (_currentTab.value) {
|
||||
DisplayMode.SHOW_SONGS -> {
|
||||
MusicMode.SONGS -> {
|
||||
settings.libSongSort = sort
|
||||
_songs.value = sort.songs(_songs.value)
|
||||
}
|
||||
DisplayMode.SHOW_ALBUMS -> {
|
||||
MusicMode.ALBUMS -> {
|
||||
settings.libAlbumSort = sort
|
||||
_albums.value = sort.albums(_albums.value)
|
||||
}
|
||||
DisplayMode.SHOW_ARTISTS -> {
|
||||
MusicMode.ARTISTS -> {
|
||||
settings.libArtistSort = sort
|
||||
_artists.value = sort.artists(_artists.value)
|
||||
}
|
||||
DisplayMode.SHOW_GENRES -> {
|
||||
MusicMode.GENRES -> {
|
||||
settings.libGenreSort = sort
|
||||
_genres.value = sort.genres(_genres.value)
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ import android.view.ViewGroup
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
@ -62,7 +62,7 @@ class AlbumListFragment : HomeListFragment<Album>() {
|
|||
val album = homeModel.albums.value[pos]
|
||||
|
||||
// Change how we display the popup depending on the mode.
|
||||
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_ALBUMS).mode) {
|
||||
return when (homeModel.getSortForTab(MusicMode.ALBUMS).mode) {
|
||||
// By Name -> Use Name
|
||||
is Sort.Mode.ByName -> album.collationKey?.run { sourceString.first().uppercase() }
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ import android.view.ViewGroup
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
@ -57,7 +57,7 @@ class ArtistListFragment : HomeListFragment<Artist>() {
|
|||
val artist = homeModel.artists.value[pos]
|
||||
|
||||
// Change how we display the popup depending on the mode.
|
||||
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_ARTISTS).mode) {
|
||||
return when (homeModel.getSortForTab(MusicMode.ARTISTS).mode) {
|
||||
// By Name -> Use Name
|
||||
is Sort.Mode.ByName -> artist.collationKey?.run { sourceString.first().uppercase() }
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ import android.view.ViewGroup
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
@ -57,7 +57,7 @@ class GenreListFragment : HomeListFragment<Genre>() {
|
|||
val genre = homeModel.genres.value[pos]
|
||||
|
||||
// Change how we display the popup depending on the mode.
|
||||
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_GENRES).mode) {
|
||||
return when (homeModel.getSortForTab(MusicMode.GENRES).mode) {
|
||||
// By Name -> Use Name
|
||||
is Sort.Mode.ByName -> genre.collationKey?.run { sourceString.first().uppercase() }
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -72,7 +72,7 @@ class SongListFragment : HomeListFragment<Song>() {
|
|||
// Change how we display the popup depending on the mode.
|
||||
// Note: We don't use the more correct individual artist name here, as sorts are largely
|
||||
// based off the names of the parent objects and not the child objects.
|
||||
return when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS).mode) {
|
||||
return when (homeModel.getSortForTab(MusicMode.SONGS).mode) {
|
||||
// Name -> Use name
|
||||
is Sort.Mode.ByName -> song.collationKey?.run { sourceString.first().uppercase() }
|
||||
|
||||
|
|
|
@ -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.Invisible
|
||||
import org.oxycblt.auxio.home.tabs.Tab.Visible
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
||||
/**
|
||||
|
@ -40,16 +40,16 @@ import org.oxycblt.auxio.util.logE
|
|||
* VTTT
|
||||
*
|
||||
* Where V is a bit representing the visibility and T is a 3-bit integer representing the
|
||||
* [DisplayMode] ordinal for this tab.
|
||||
* [MusicMode] ordinal for this tab.
|
||||
*
|
||||
* To serialize and deserialize a tab sequence, [toSequence] and [fromSequence] can be used
|
||||
* respectively.
|
||||
*
|
||||
* By default, the tab order will be SONGS, ALBUMS, ARTISTS, GENRES, PLAYLISTS
|
||||
*/
|
||||
sealed class Tab(open val mode: DisplayMode) {
|
||||
data class Visible(override val mode: DisplayMode) : Tab(mode)
|
||||
data class Invisible(override val mode: DisplayMode) : Tab(mode)
|
||||
sealed class Tab(open val mode: MusicMode) {
|
||||
data class Visible(override val mode: MusicMode) : Tab(mode)
|
||||
data class Invisible(override val mode: MusicMode) : Tab(mode)
|
||||
|
||||
companion object {
|
||||
/** The length a well-formed tab sequence should be */
|
||||
|
@ -59,14 +59,14 @@ sealed class Tab(open val mode: DisplayMode) {
|
|||
const val SEQUENCE_DEFAULT = 0b1000_1001_1010_1011_0100
|
||||
|
||||
/**
|
||||
* Maps between the integer code in the tab sequence and the actual [DisplayMode] instance.
|
||||
* Maps between the integer code in the tab sequence and the actual [MusicMode] instance.
|
||||
*/
|
||||
private val MODE_TABLE =
|
||||
arrayOf(
|
||||
DisplayMode.SHOW_SONGS,
|
||||
DisplayMode.SHOW_ALBUMS,
|
||||
DisplayMode.SHOW_ARTISTS,
|
||||
DisplayMode.SHOW_GENRES
|
||||
MusicMode.SONGS,
|
||||
MusicMode.ALBUMS,
|
||||
MusicMode.ARTISTS,
|
||||
MusicMode.GENRES
|
||||
)
|
||||
|
||||
/** Convert an array [tabs] into a sequence of tabs. */
|
||||
|
|
|
@ -23,7 +23,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemTabBinding
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.ui.recycler.DialogViewHolder
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
|
@ -59,7 +59,7 @@ class TabAdapter(private val listener: Listener) : RecyclerView.Adapter<TabViewH
|
|||
}
|
||||
|
||||
interface Listener {
|
||||
fun onVisibilityToggled(displayMode: DisplayMode)
|
||||
fun onVisibilityToggled(mode: MusicMode)
|
||||
fun onPickUpTab(viewHolder: RecyclerView.ViewHolder)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogTabsBinding
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
@ -79,11 +79,8 @@ class TabCustomizeDialog : ViewBindingDialogFragment<DialogTabsBinding>(), TabAd
|
|||
binding.tabRecycler.adapter = null
|
||||
}
|
||||
|
||||
override fun onVisibilityToggled(displayMode: DisplayMode) {
|
||||
// Tab viewholders bind with the initial tab state, which will drift from the actual
|
||||
// state of the tabs over editing. So, this callback simply provides the displayMode
|
||||
// for us to locate within the data and then update.
|
||||
val index = tabAdapter.tabs.indexOfFirst { it.mode == displayMode }
|
||||
override fun onVisibilityToggled(mode: MusicMode) {
|
||||
val index = tabAdapter.tabs.indexOfFirst { it.mode == mode }
|
||||
if (index > -1) {
|
||||
val tab = tabAdapter.tabs[index]
|
||||
tabAdapter.setTab(
|
||||
|
|
|
@ -24,6 +24,7 @@ import coil.imageLoader
|
|||
import coil.request.Disposable
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import org.oxycblt.auxio.image.extractor.SquareFrameTransform
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,6 +66,18 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
}
|
||||
}
|
||||
|
||||
var isPlaying: Boolean
|
||||
get() = drawable == playingIndicatorDrawable
|
||||
set(value) {
|
||||
if (value) {
|
||||
playingIndicatorDrawable.start()
|
||||
setImageDrawable(playingIndicatorDrawable)
|
||||
} else {
|
||||
playingIndicatorDrawable.stop()
|
||||
setImageDrawable(pausedIndicatorDrawable)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// Use clipToOutline and a background drawable to crop images. While Coil's transformation
|
||||
// could theoretically be used to round corners, the corner radius is dependent on the
|
||||
|
@ -87,15 +99,13 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
// Emulate StyledDrawable scaling with matrix scaling.
|
||||
val iconSize = max(measuredWidth, measuredHeight) / 2
|
||||
|
||||
imageMatrix =
|
||||
indicatorMatrix.apply {
|
||||
reset()
|
||||
drawable?.let { drawable ->
|
||||
// Android is too good to allow us to set a fixed image size, so we instead need
|
||||
// to define a matrix to scale an image directly.
|
||||
|
||||
// First scale the icon up to the desired size.
|
||||
indicatorMatrixSrc.set(
|
||||
0f,
|
||||
|
@ -119,16 +129,4 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isPlaying: Boolean
|
||||
get() = drawable == playingIndicatorDrawable
|
||||
set(value) {
|
||||
if (value) {
|
||||
playingIndicatorDrawable.start()
|
||||
setImageDrawable(playingIndicatorDrawable)
|
||||
} else {
|
||||
playingIndicatorDrawable.stop()
|
||||
setImageDrawable(pausedIndicatorDrawable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import coil.dispose
|
|||
import coil.load
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.image.extractor.SquareFrameTransform
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.image
|
||||
package org.oxycblt.auxio.image.extractor
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.image
|
||||
package org.oxycblt.auxio.image.extractor
|
||||
|
||||
import android.content.Context
|
||||
import coil.ImageLoader
|
||||
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import kotlin.math.min
|
||||
|
||||
/** A basic keyer for music data. */
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.image
|
||||
package org.oxycblt.auxio.image.extractor
|
||||
|
||||
import coil.decode.DataSource
|
||||
import coil.drawable.CrossfadeDrawable
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.image
|
||||
package org.oxycblt.auxio.image.extractor
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import coil.size.Size
|
|
@ -26,7 +26,6 @@ import kotlinx.parcelize.Parcelize
|
|||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Date.Companion.from
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.inRangeOrNull
|
||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||
|
@ -59,10 +58,12 @@ sealed class Music : Item {
|
|||
|
||||
/**
|
||||
* A key used by the sorting system that takes into account the sort tags of this item,
|
||||
* any (english) articles that prefix the names, and collation rules. Lazily generated
|
||||
* since generating a collation key is non-trivial.
|
||||
* any (english) articles that prefix the names, and collation rules.
|
||||
*/
|
||||
val collationKey: CollationKey? by lazy {
|
||||
// Ideally, we would generate this on creation, but this is an abstract class, which
|
||||
// requires us to generate it lazily instead.
|
||||
|
||||
val sortName = (rawSortName ?: rawName)?.run {
|
||||
when {
|
||||
length > 5 && startsWith("the ", ignoreCase = true) -> substring(4)
|
||||
|
@ -125,7 +126,8 @@ sealed class Music : Item {
|
|||
*/
|
||||
fun hashed(clazz: KClass<*>, updates: MessageDigest.() -> Unit): UID {
|
||||
// Auxio hashes consist of the MD5 hash of the non-subjective, consistent
|
||||
// tags in a music item. For easier use with MusicBrainz IDs, we
|
||||
// tags in a music item. For easier use with MusicBrainz IDs, we transform
|
||||
// this into a UUID too.
|
||||
val digest = MessageDigest.getInstance("MD5")
|
||||
updates(digest)
|
||||
val uuid = digest.digest().toUuid()
|
||||
|
|
66
app/src/main/java/org/oxycblt/auxio/music/MusicMode.kt
Normal file
66
app/src/main/java/org/oxycblt/auxio/music/MusicMode.kt
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
enum class MusicMode {
|
||||
SONGS,
|
||||
ALBUMS,
|
||||
ARTISTS,
|
||||
GENRES;
|
||||
|
||||
val string: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SONGS -> R.string.lbl_songs
|
||||
ALBUMS -> R.string.lbl_albums
|
||||
ARTISTS -> R.string.lbl_artists
|
||||
GENRES -> R.string.lbl_genres
|
||||
}
|
||||
|
||||
val icon: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SONGS -> R.drawable.ic_song_24
|
||||
ALBUMS -> R.drawable.ic_album_24
|
||||
ARTISTS -> R.drawable.ic_artist_24
|
||||
GENRES -> R.drawable.ic_genre_24
|
||||
}
|
||||
|
||||
val intCode: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SONGS -> IntegerTable.MUSIC_MODE_SONGS
|
||||
ALBUMS -> IntegerTable.MUSIC_MODE_ALBUMS
|
||||
ARTISTS -> IntegerTable.MUSIC_MODE_ARTISTS
|
||||
GENRES -> IntegerTable.MUSIC_MODE_GENRES
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int) =
|
||||
when (value) {
|
||||
IntegerTable.MUSIC_MODE_SONGS -> SONGS
|
||||
IntegerTable.MUSIC_MODE_ALBUMS -> ALBUMS
|
||||
IntegerTable.MUSIC_MODE_ARTISTS -> ARTISTS
|
||||
IntegerTable.MUSIC_MODE_GENRES -> GENRES
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,18 +15,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.ui
|
||||
package org.oxycblt.auxio.music
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
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.ui.Sort.Mode
|
||||
import org.oxycblt.auxio.music.Sort.Mode
|
||||
|
||||
/**
|
||||
* Represents the sort modes used in Auxio.
|
|
@ -22,14 +22,12 @@ import org.oxycblt.auxio.music.Song
|
|||
/** TODO: Stub class, not implemented yet */
|
||||
class CacheLayer {
|
||||
fun init() {
|
||||
// STUB: Add cache database
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a list of newly-indexed raw songs to the database.
|
||||
*/
|
||||
fun finalize(rawSongs: List<Song.Raw>) {
|
||||
// STUB: Add cache database
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,7 +55,7 @@ import java.io.File
|
|||
* straightforward for this contract that was dropped on it's head as a baby. So instead, you have
|
||||
* to query for each genre, query all the songs in each genre, and then iterate through those songs
|
||||
* to link every song with their genre. This is not documented anywhere, and the O(mom im scared)
|
||||
* algorithm you have to run to get it working single-handedly DOUBLES Auxio's loading times. At no
|
||||
* algorithm you have to run to get it working single-handedly DOUBLES Auxio's query times. At no
|
||||
* point have the devs considered that this system is absolutely insane, and instead focused on
|
||||
* adding infuriat- I mean nice proprietary extensions to MediaStore for their own Google Play
|
||||
* Music, and of course every Google Play Music user knew how great that turned out!
|
||||
|
@ -347,6 +347,7 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
raw.albumArtistNames =
|
||||
cursor.getStringOrNull(albumArtistIndex)?.maybeParseSeparators(settings)
|
||||
|
||||
// Get the genre value we had to query for in initialization
|
||||
raw.genreNames = genreNamesMap[raw.mediaStoreId]
|
||||
}
|
||||
|
||||
|
@ -381,6 +382,7 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
|
||||
/**
|
||||
* A [MediaStoreLayer] that completes the music loading process in a way compatible from
|
||||
* API 21 onwards to API 29.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class Api21MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
||||
|
|
|
@ -247,6 +247,9 @@ class Task(context: Context, private val settings: Settings, private val raw: So
|
|||
tags["TORY"]?.run { get(0).toIntOrNull() }
|
||||
?: tags["TYER"]?.run { get(0).toIntOrNull() } ?: return null
|
||||
|
||||
// Assume that TDAT/TIME can refer to TYER or TORY depending on if TORY
|
||||
// is present.
|
||||
|
||||
val tdat = tags["TDAT"]
|
||||
return if (tdat != null && tdat[0].length == 4 && tdat[0].isDigitsOnly()) {
|
||||
val mm = tdat[0].substring(0..1).toInt()
|
||||
|
|
|
@ -32,12 +32,12 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
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.Api29MediaStoreLayer
|
||||
import org.oxycblt.auxio.music.extractor.Api30MediaStoreLayer
|
||||
import org.oxycblt.auxio.music.extractor.CacheLayer
|
||||
import org.oxycblt.auxio.music.extractor.MetadataLayer
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
|
|
@ -64,7 +64,7 @@ class IndexingNotification(private val context: Context) :
|
|||
|
||||
lastUpdateTime = SystemClock.elapsedRealtime()
|
||||
|
||||
// Only update the notification every two seconds to prevent rate-limiting.
|
||||
// Only update the notification every 1.5s to prevent rate-limiting.
|
||||
logD("Updating state to $indexing")
|
||||
setContentText(
|
||||
context.getString(R.string.fmt_indexing, indexing.current, indexing.total)
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.playback
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
|
||||
/**
|
||||
* Enum that indicates how the queue should be constructed.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
enum class PlaybackMode {
|
||||
/** Construct the queue from the genre's songs */
|
||||
ALL_SONGS,
|
||||
|
||||
/** Construct the queue from the artist's songs */
|
||||
IN_ALBUM,
|
||||
|
||||
/** Construct the queue from the album's songs */
|
||||
IN_ARTIST,
|
||||
|
||||
/** Construct the queue from all songs */
|
||||
IN_GENRE;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Get a [PlaybackMode] for an int [constant]
|
||||
* @return The mode, null if there isn't one for this.
|
||||
*/
|
||||
fun fromInt(constant: Int) =
|
||||
when (constant) {
|
||||
IntegerTable.PLAYBACK_MODE_ALL_SONGS -> ALL_SONGS
|
||||
IntegerTable.PLAYBACK_MODE_IN_ALBUM -> IN_ALBUM
|
||||
IntegerTable.PLAYBACK_MODE_IN_ARTIST -> IN_ARTIST
|
||||
IntegerTable.PLAYBACK_MODE_IN_GENRE -> IN_GENRE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.oxycblt.auxio.music.MusicParent
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
|
|
@ -28,6 +28,7 @@ import kotlinx.coroutines.launch
|
|||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.dsToMs
|
||||
|
@ -92,15 +93,15 @@ class PlaybackViewModel(application: Application) :
|
|||
// --- PLAYING FUNCTIONS ---
|
||||
|
||||
/** Play a [song] with the [mode] specified, */
|
||||
fun play(song: Song, mode: PlaybackMode) {
|
||||
fun play(song: Song, mode: MusicMode) {
|
||||
// TODO: Remove this function when selection is implemented
|
||||
|
||||
val parent =
|
||||
when (mode) {
|
||||
PlaybackMode.IN_ALBUM -> song.album
|
||||
PlaybackMode.IN_ARTIST -> song.album.artist
|
||||
PlaybackMode.IN_GENRE -> song.genres.maxBy { it.songs.size }
|
||||
PlaybackMode.ALL_SONGS -> null
|
||||
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)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback
|
||||
package org.oxycblt.auxio.playback.ui
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback
|
||||
package org.oxycblt.auxio.playback.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback
|
||||
package org.oxycblt.auxio.playback.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.LayerDrawable
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback
|
||||
package org.oxycblt.auxio.playback.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
@ -67,7 +67,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
|||
// this component.
|
||||
val from = max(value, 0)
|
||||
|
||||
// Sanity check: Ensure that this value is within the duration and will not crash
|
||||
// Sanity check 2: Ensure that this value is within the duration and will not crash
|
||||
// the app, and that the user is not currently seeking (which would cause the SeekBar
|
||||
// to jump around).
|
||||
if (from <= durationDs && !isActivated) {
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
|
@ -78,7 +79,15 @@ class SearchFragment :
|
|||
|
||||
override fun onBindingCreated(binding: FragmentSearchBinding, savedInstanceState: Bundle?) {
|
||||
binding.searchToolbar.apply {
|
||||
menu.findItem(searchModel.filterMode?.itemId ?: R.id.option_filter_all).isChecked = true
|
||||
val itemIdToSelect = when (searchModel.filterMode) {
|
||||
MusicMode.SONGS -> R.id.option_filter_songs
|
||||
MusicMode.ALBUMS -> R.id.option_filter_albums
|
||||
MusicMode.ARTISTS -> R.id.option_filter_artists
|
||||
MusicMode.GENRES -> R.id.option_filter_genres
|
||||
null -> R.id.option_filter_all
|
||||
}
|
||||
|
||||
menu.findItem(itemIdToSelect).isChecked = true
|
||||
|
||||
setNavigationOnClickListener {
|
||||
imm.hide()
|
||||
|
|
|
@ -33,11 +33,11 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.application
|
||||
|
@ -59,7 +59,7 @@ class SearchViewModel(application: Application) :
|
|||
val searchResults: StateFlow<List<Item>>
|
||||
get() = _searchResults
|
||||
|
||||
val filterMode: DisplayMode?
|
||||
val filterMode: MusicMode?
|
||||
get() = settings.searchFilterMode
|
||||
|
||||
private var lastQuery: String? = null
|
||||
|
@ -89,28 +89,28 @@ class SearchViewModel(application: Application) :
|
|||
|
||||
// Note: a filter mode of null means to not filter at all.
|
||||
|
||||
if (filterMode == null || filterMode == DisplayMode.SHOW_ARTISTS) {
|
||||
if (filterMode == null || filterMode == MusicMode.ARTISTS) {
|
||||
library.artists.filterArtistsBy(query)?.let { artists ->
|
||||
results.add(Header(R.string.lbl_artists))
|
||||
results.addAll(sort.artists(artists))
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMode == null || filterMode == DisplayMode.SHOW_ALBUMS) {
|
||||
if (filterMode == null || filterMode == MusicMode.ALBUMS) {
|
||||
library.albums.filterAlbumsBy(query)?.let { albums ->
|
||||
results.add(Header(R.string.lbl_albums))
|
||||
results.addAll(sort.albums(albums))
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMode == null || filterMode == DisplayMode.SHOW_GENRES) {
|
||||
if (filterMode == null || filterMode == MusicMode.GENRES) {
|
||||
library.genres.filterGenresBy(query)?.let { genres ->
|
||||
results.add(Header(R.string.lbl_genres))
|
||||
results.addAll(sort.genres(genres))
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMode == null || filterMode == DisplayMode.SHOW_SONGS) {
|
||||
if (filterMode == null || filterMode == MusicMode.SONGS) {
|
||||
library.songs.filterSongsBy(query)?.let { songs ->
|
||||
results.add(Header(R.string.lbl_songs))
|
||||
results.addAll(sort.songs(songs))
|
||||
|
@ -128,10 +128,10 @@ class SearchViewModel(application: Application) :
|
|||
fun updateFilterModeWithId(@IdRes id: Int) {
|
||||
val newFilterMode =
|
||||
when (id) {
|
||||
R.id.option_filter_songs -> DisplayMode.SHOW_SONGS
|
||||
R.id.option_filter_albums -> DisplayMode.SHOW_ALBUMS
|
||||
R.id.option_filter_artists -> DisplayMode.SHOW_ARTISTS
|
||||
R.id.option_filter_genres -> DisplayMode.SHOW_GENRES
|
||||
R.id.option_filter_songs -> MusicMode.SONGS
|
||||
R.id.option_filter_albums -> MusicMode.ALBUMS
|
||||
R.id.option_filter_artists -> MusicMode.ARTISTS
|
||||
R.id.option_filter_genres -> MusicMode.GENRES
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
|
|
@ -21,21 +21,21 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.storage.StorageManager
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.home.tabs.Tab
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.Sort
|
||||
import org.oxycblt.auxio.music.dirs.MusicDirs
|
||||
import org.oxycblt.auxio.playback.BarAction
|
||||
import org.oxycblt.auxio.playback.PlaybackMode
|
||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
|
||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.accent.Accent
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,67 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to migrate shared preference keys to their new versions. Only intended for use by
|
||||
* AuxioApp. Compat code will persist for 6 months before being removed.
|
||||
*/
|
||||
fun migrate() {
|
||||
if (inner.contains(OldKeys.KEY_ACCENT3)) {
|
||||
logD("Migrating ${OldKeys.KEY_ACCENT3}")
|
||||
|
||||
var accent = inner.getInt(OldKeys.KEY_ACCENT3, 5)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Accents were previously frozen as soon as the OS was updated to android twelve,
|
||||
// as dynamic colors were enabled by default. This is no longer the case, so we need
|
||||
// to re-update the setting to dynamic colors here.
|
||||
accent = 16
|
||||
}
|
||||
|
||||
inner.edit {
|
||||
putInt(context.getString(R.string.set_accent), accent)
|
||||
remove(OldKeys.KEY_ACCENT3)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.migratePlaybackMode() =
|
||||
when (this) {
|
||||
IntegerTable.PLAYBACK_MODE_ALL_SONGS -> MusicMode.SONGS
|
||||
IntegerTable.PLAYBACK_MODE_IN_GENRE -> MusicMode.GENRES
|
||||
IntegerTable.PLAYBACK_MODE_IN_ARTIST -> MusicMode.ARTISTS
|
||||
IntegerTable.PLAYBACK_MODE_IN_ALBUM -> MusicMode.ALBUMS
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (inner.contains(OldKeys.KEY_LIB_PLAYBACK_MODE)) {
|
||||
logD("Migrating ${OldKeys.KEY_LIB_PLAYBACK_MODE}")
|
||||
|
||||
val mode = inner.getInt(
|
||||
OldKeys.KEY_LIB_PLAYBACK_MODE,
|
||||
IntegerTable.PLAYBACK_MODE_ALL_SONGS
|
||||
).migratePlaybackMode() ?: MusicMode.SONGS
|
||||
|
||||
inner.edit {
|
||||
putInt(context.getString(R.string.set_key_library_song_playback_mode), mode.intCode)
|
||||
remove(OldKeys.KEY_LIB_PLAYBACK_MODE)
|
||||
}
|
||||
}
|
||||
|
||||
if (inner.contains(OldKeys.KEY_DETAIL_PLAYBACK_MODE)) {
|
||||
logD("Migrating ${OldKeys.KEY_DETAIL_PLAYBACK_MODE}")
|
||||
|
||||
val mode = inner.getInt(OldKeys.KEY_DETAIL_PLAYBACK_MODE, Int.MIN_VALUE).migratePlaybackMode()
|
||||
|
||||
inner.edit {
|
||||
putInt(
|
||||
context.getString(R.string.set_key_detail_song_playback_mode),
|
||||
mode?.intCode ?: Int.MIN_VALUE
|
||||
)
|
||||
remove(OldKeys.KEY_DETAIL_PLAYBACK_MODE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun release() {
|
||||
inner.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
@ -86,7 +147,7 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
|
||||
/** The current accent. */
|
||||
var accent: Accent
|
||||
get() = handleAccentCompat(context, inner)
|
||||
get() = Accent.from(inner.getInt(context.getString(R.string.set_accent), Accent.DEFAULT))
|
||||
set(value) {
|
||||
inner.edit {
|
||||
putInt(context.getString(R.string.set_key_accent), value.index)
|
||||
|
@ -163,23 +224,23 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
}
|
||||
|
||||
/** What queue to create when a song is selected from the library or search */
|
||||
val libPlaybackMode: PlaybackMode
|
||||
val libPlaybackMode: MusicMode
|
||||
get() =
|
||||
PlaybackMode.fromInt(
|
||||
MusicMode.fromInt(
|
||||
inner.getInt(
|
||||
context.getString(R.string.set_key_library_song_playback_mode),
|
||||
Int.MIN_VALUE
|
||||
)
|
||||
)
|
||||
?: PlaybackMode.ALL_SONGS
|
||||
?: MusicMode.SONGS
|
||||
|
||||
/**
|
||||
* What queue t create when a song is selected from an album/artist/genre. Null means to default
|
||||
* to the currently shown item.
|
||||
*/
|
||||
val detailPlaybackMode: PlaybackMode?
|
||||
val detailPlaybackMode: MusicMode?
|
||||
get() =
|
||||
PlaybackMode.fromInt(
|
||||
MusicMode.fromInt(
|
||||
inner.getInt(
|
||||
context.getString(R.string.set_key_detail_song_playback_mode),
|
||||
Int.MIN_VALUE
|
||||
|
@ -246,9 +307,9 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
}
|
||||
|
||||
/** The current filter mode of the search tab */
|
||||
var searchFilterMode: DisplayMode?
|
||||
var searchFilterMode: MusicMode?
|
||||
get() =
|
||||
DisplayMode.fromInt(
|
||||
MusicMode.fromInt(
|
||||
inner.getInt(context.getString(R.string.set_key_search_filter), Int.MIN_VALUE)
|
||||
)
|
||||
set(value) {
|
||||
|
@ -370,35 +431,11 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- COMPAT ---
|
||||
|
||||
fun handleAccentCompat(context: Context, prefs: SharedPreferences): Accent {
|
||||
val currentKey = context.getString(R.string.set_key_accent)
|
||||
|
||||
if (prefs.contains(OldKeys.KEY_ACCENT3)) {
|
||||
Log.d("Auxio.SettingsCompat", "Migrating ${OldKeys.KEY_ACCENT3}")
|
||||
|
||||
var accent = prefs.getInt(OldKeys.KEY_ACCENT3, 5)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Accents were previously frozen as soon as the OS was updated to android twelve,
|
||||
// as dynamic colors were enabled by default. This is no longer the case, so we need
|
||||
// to re-update the setting to dynamic colors here.
|
||||
accent = 16
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(currentKey, accent)
|
||||
remove(OldKeys.KEY_ACCENT3)
|
||||
apply()
|
||||
}
|
||||
/** Cache of the old keys used in Auxio. */
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT3 = "auxio_accent"
|
||||
const val KEY_LIB_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2"
|
||||
const val KEY_DETAIL_PLAYBACK_MODE = "auxio_detail_song_play_mode"
|
||||
}
|
||||
|
||||
return Accent.from(prefs.getInt(currentKey, Accent.DEFAULT))
|
||||
}
|
||||
|
||||
/** Cache of the old keys used in Auxio. */
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT3 = "auxio_accent"
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.ui
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
/**
|
||||
* An enum for determining what items to show in a given list. Note: **DO NOT RE-ARRANGE THE ENUM**.
|
||||
* The ordinals are used to store library tabs, so doing changing them would also change the meaning
|
||||
* of tab instances.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
enum class DisplayMode {
|
||||
SHOW_SONGS,
|
||||
SHOW_ALBUMS,
|
||||
SHOW_ARTISTS,
|
||||
SHOW_GENRES;
|
||||
|
||||
val string: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SHOW_SONGS -> R.string.lbl_songs
|
||||
SHOW_ALBUMS -> R.string.lbl_albums
|
||||
SHOW_ARTISTS -> R.string.lbl_artists
|
||||
SHOW_GENRES -> R.string.lbl_genres
|
||||
}
|
||||
|
||||
val icon: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SHOW_SONGS -> R.drawable.ic_song_24
|
||||
SHOW_ALBUMS -> R.drawable.ic_album_24
|
||||
SHOW_ARTISTS -> R.drawable.ic_artist_24
|
||||
SHOW_GENRES -> R.drawable.ic_genre_24
|
||||
}
|
||||
|
||||
val itemId: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SHOW_SONGS -> R.id.option_filter_songs
|
||||
SHOW_ALBUMS -> R.id.option_filter_albums
|
||||
SHOW_ARTISTS -> R.id.option_filter_artists
|
||||
SHOW_GENRES -> R.id.option_filter_genres
|
||||
}
|
||||
|
||||
val intCode: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SHOW_SONGS -> IntegerTable.DISPLAY_MODE_SHOW_SONGS
|
||||
SHOW_ALBUMS -> IntegerTable.DISPLAY_MODE_SHOW_ALBUMS
|
||||
SHOW_ARTISTS -> IntegerTable.DISPLAY_MODE_SHOW_ARTISTS
|
||||
SHOW_GENRES -> IntegerTable.DISPLAY_MODE_SHOW_GENRES
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Convert a filtering integer to a [DisplayMode]. In this context, a null value means to
|
||||
* filter nothing.
|
||||
* @return A [DisplayMode] for this constant (including null)
|
||||
*/
|
||||
fun fromInt(value: Int): DisplayMode? {
|
||||
return when (value) {
|
||||
IntegerTable.DISPLAY_MODE_SHOW_SONGS -> SHOW_SONGS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_GENRES -> SHOW_GENRES
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import coil.request.ImageRequest
|
|||
import coil.transform.RoundedCornersTransformation
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.image.BitmapProvider
|
||||
import org.oxycblt.auxio.image.SquareFrameTransform
|
||||
import org.oxycblt.auxio.image.extractor.SquareFrameTransform
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.InternalPlayer
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
tools:text="Album Name" />
|
||||
|
||||
|
||||
<org.oxycblt.auxio.playback.StyledSeekBar
|
||||
<org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
android:id="@+id/playback_seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -73,7 +73,7 @@
|
|||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<org.oxycblt.auxio.playback.ForcedLTRFrameLayout
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_controls_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -111,7 +111,7 @@
|
|||
app:layout_constraintStart_toEndOf="@+id/playback_repeat"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<org.oxycblt.auxio.playback.AnimatedMaterialButton
|
||||
<org.oxycblt.auxio.playback.ui.AnimatedMaterialButton
|
||||
android:id="@+id/playback_play_pause"
|
||||
style="@style/Widget.Auxio.Button.PlayPause"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -151,6 +151,6 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</org.oxycblt.auxio.playback.ForcedLTRFrameLayout>
|
||||
</org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
tools:text="Album Name" />
|
||||
|
||||
|
||||
<org.oxycblt.auxio.playback.StyledSeekBar
|
||||
<org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
android:id="@+id/playback_seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -75,7 +75,7 @@
|
|||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<org.oxycblt.auxio.playback.ForcedLTRFrameLayout
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_controls_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -114,7 +114,7 @@
|
|||
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<org.oxycblt.auxio.playback.AnimatedMaterialButton
|
||||
<org.oxycblt.auxio.playback.ui.AnimatedMaterialButton
|
||||
android:id="@+id/playback_play_pause"
|
||||
style="@style/Widget.Auxio.Button.PlayPause"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -154,6 +154,6 @@
|
|||
app:tint="@color/sel_accented" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</org.oxycblt.auxio.playback.ForcedLTRFrameLayout>
|
||||
</org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
android:id="@+id/playback_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.PlaybackSheetBehavior">
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/playback_bar_fragment"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
style="@style/Widget.Auxio.DisableDropShadows"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.PlaybackSheetBehavior">
|
||||
app:layout_behavior="org.oxycblt.auxio.playback.ui.PlaybackSheetBehavior">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/playback_bar_fragment"
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/playback_song"
|
||||
tools:text="Artist Name / Album Name" />
|
||||
|
||||
<org.oxycblt.auxio.playback.ForcedLTRFrameLayout
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_controls_wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -73,9 +73,9 @@
|
|||
tools:icon="@drawable/ic_skip_next_24" />
|
||||
</LinearLayout>
|
||||
|
||||
</org.oxycblt.auxio.playback.ForcedLTRFrameLayout>
|
||||
</org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout>
|
||||
|
||||
<org.oxycblt.auxio.playback.ForcedLTRFrameLayout
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_progress_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -91,6 +91,6 @@
|
|||
android:layout_marginEnd="@dimen/spacing_small"
|
||||
tools:progress="70" />
|
||||
|
||||
</org.oxycblt.auxio.playback.ForcedLTRFrameLayout>
|
||||
</org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/playback_artist"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<org.oxycblt.auxio.playback.StyledSeekBar
|
||||
<org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
android:id="@+id/playback_seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -87,7 +87,7 @@
|
|||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<org.oxycblt.auxio.playback.ForcedLTRFrameLayout
|
||||
<org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout
|
||||
android:id="@+id/playback_controls_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -126,7 +126,7 @@
|
|||
app:layout_constraintStart_toEndOf="@+id/playback_repeat"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<org.oxycblt.auxio.playback.AnimatedMaterialButton
|
||||
<org.oxycblt.auxio.playback.ui.AnimatedMaterialButton
|
||||
android:id="@+id/playback_play_pause"
|
||||
style="@style/Widget.Auxio.Button.PlayPause"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -166,7 +166,7 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</org.oxycblt.auxio.playback.ForcedLTRFrameLayout>
|
||||
</org.oxycblt.auxio.playback.ui.ForcedLTRFrameLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
<string name="set_key_pre_amp_with" translatable="false">auxio_pre_amp_with</string>
|
||||
<string name="set_key_pre_amp_without" translatable="false">auxio_pre_amp_without</string>
|
||||
|
||||
<string name="set_key_library_song_playback_mode" translatable="false">KEY_SONG_PLAY_MODE2</string>
|
||||
<string name="set_key_detail_song_playback_mode" translatable="false">auxio_detail_song_play_mode</string>
|
||||
<string name="set_key_library_song_playback_mode" translatable="false">auxio_library_playback_mode</string>
|
||||
<string name="set_key_detail_song_playback_mode" translatable="false">auxio_detail_playback_mode</string>
|
||||
<string name="set_key_keep_shuffle" translatable="false">KEY_KEEP_SHUFFLE</string>
|
||||
<string name="set_key_rewind_prev" translatable="false">KEY_PREV_REWIND</string>
|
||||
<string name="set_key_repeat_pause" translatable="false">KEY_LOOP_PAUSE</string>
|
||||
|
@ -127,10 +127,10 @@
|
|||
<integer name="bar_action_shuffle">0xA11B</integer>
|
||||
|
||||
<integer name="play_mode_none">-2147483648</integer>
|
||||
<integer name="play_mode_genre">0xA103</integer>
|
||||
<integer name="play_mode_artist">0xA104</integer>
|
||||
<integer name="play_mode_album">0xA105</integer>
|
||||
<integer name="play_mode_songs">0xA106</integer>
|
||||
<integer name="play_mode_genre">0xA108</integer>
|
||||
<integer name="play_mode_artist">0xA109</integer>
|
||||
<integer name="play_mode_album">0xA10A</integer>
|
||||
<integer name="play_mode_songs">0xA10B</integer>
|
||||
|
||||
<integer name="replay_gain_track">0xA111</integer>
|
||||
<integer name="replay_gain_album">0xA112</integer>
|
||||
|
|
|
@ -120,7 +120,7 @@ also the `detail`-specific `DiscHeader` and `SortHeader`, however these are larg
|
|||
|
||||
Other data types represent a specific UI configuration or state:
|
||||
- Data structures like `Sort` contain an ascending state that can be modified immutably.
|
||||
- Enums like `DisplayMode` and `RepeatMode` only contain static data, such as a string resource.
|
||||
- Enums like `MusicMode` and `RepeatMode` only contain static data, such as a string resource.
|
||||
|
||||
Things to keep in mind while working with music data:
|
||||
- `id` is not derived from the `MediaStore` ID of the music data. It is actually a hash of the
|
||||
|
@ -253,17 +253,15 @@ The major classes are:
|
|||
library should use this.
|
||||
- `Indexer`, which manages how music is loaded. This is only used by code that must manage or
|
||||
mirror the music loading state.
|
||||
- The extractor system, which is Auxio's music parser. It's structured as several "Layer" classes
|
||||
that build on eachother to implement version-specific functionality.
|
||||
|
||||
Internally, there are several other major systems:
|
||||
- `IndexerService`, which does the indexer work in the background.
|
||||
- `Indexer.Backend` implementations, which actually talk to the media database and load music.
|
||||
As it stands, there are two classes of backend:
|
||||
- Version-specific `MediaStoreBackend` implementations, which transform the (often insane)
|
||||
music data from Android into a usable `Song`.
|
||||
- `ExoPlayerBackend`, which mutates audio with extracted ID3v2 and Vorbis tags. This enables
|
||||
some extra features and side-steps unfixable issues with `MediaStore`
|
||||
- `StorageFramework`, which is a group of utilities that allows Auxio to be volume-aware and to
|
||||
work with both extension-based and format-based mime types.
|
||||
- Configuration models like `MusicMode` and `Sort`, which are tangentally related to operations
|
||||
done on music
|
||||
|
||||
The music loading process is roughly as follows:
|
||||
1. Something triggers `IndexerService` to start indexing, either by the UI or by the service itself
|
||||
|
@ -313,8 +311,6 @@ Shared views and view configuration models. This contains:
|
|||
- Important `Fragment` superclasses like `ViewBindingFragment` and `MenuFragment`
|
||||
- Customized views such as `AuxioAppBarLayout`, and others, which fix shortcomings with the
|
||||
default implementations.
|
||||
- Configuration models like `DisplayMode` and `Sort`, which are used in many places but aren't tied
|
||||
to a specific feature.
|
||||
- `ForegroundManager` and `ServiceNotification`, which remove boilerplate regarding service
|
||||
foreground instantiation.
|
||||
- The `RecyclerView` adapter framework described previously.
|
||||
|
|
Loading…
Reference in a new issue