deps: update
Spotless -> 6.15.0 Core -> 1.9.0
This commit is contained in:
parent
d6e7b99e1f
commit
83c5b85424
158 changed files with 889 additions and 226 deletions
|
@ -82,7 +82,7 @@ dependencies {
|
|||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
||||
implementation 'androidx.core:core-ktx:+'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
|
||||
// Lifecycle
|
||||
def lifecycle_version = "2.5.1"
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.oxycblt.auxio.ui.UISettings
|
|||
|
||||
/**
|
||||
* A simple, rational music player for android.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltAndroidApp
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.oxycblt.auxio
|
|||
/**
|
||||
* A table containing all of the magic integer codes that the codebase has currently reserved. May
|
||||
* be non-contiguous.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
object IntegerTable {
|
||||
|
|
|
@ -40,17 +40,13 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
/**
|
||||
* Auxio's single [AppCompatActivity].
|
||||
*
|
||||
* TODO: Add error screens
|
||||
*
|
||||
* TODO: Custom language support
|
||||
*
|
||||
* TODO: Use proper material attributes (Not the weird dimen attributes I currently have)
|
||||
*
|
||||
* TODO: Migrate to material animation system
|
||||
*
|
||||
* TODO: Unit testing
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Add error screens
|
||||
* TODO: Custom language support
|
||||
* TODO: Use proper material attributes (Not the weird dimen attributes I currently have)
|
||||
* TODO: Migrate to material animation system
|
||||
* TODO: Unit testing
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
@ -112,6 +108,7 @@ class MainActivity : AppCompatActivity() {
|
|||
/**
|
||||
* Transform an [Intent] given to [MainActivity] into a [InternalPlayer.Action] that can be used
|
||||
* in the playback system.
|
||||
*
|
||||
* @param intent The (new) [Intent] given to this [MainActivity], or null if there is no intent.
|
||||
* @return true If the analogous [InternalPlayer.Action] to the given [Intent] was started,
|
||||
* false otherwise.
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.oxycblt.auxio.util.*
|
|||
/**
|
||||
* A wrapper around the home fragment that shows the playback fragment and controls the more
|
||||
* high-level navigation features.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.oxycblt.auxio.util.*
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows information about an [Album].
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows information about an [Artist].
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.oxycblt.auxio.util.*
|
|||
/**
|
||||
* [ViewModel] that manages the Song, Album, Artist, and Genre detail views. Keeps track of the
|
||||
* current item they are showing, sub-data to display, and configuration.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -182,6 +183,7 @@ constructor(
|
|||
/**
|
||||
* Set a new [currentSong] from it's [Music.UID]. If the [Music.UID] differs, [currentSong] and
|
||||
* [songAudioInfo] will be updated to align with the new [Song].
|
||||
*
|
||||
* @param uid The UID of the [Song] to load. Must be valid.
|
||||
*/
|
||||
fun setSongUid(uid: Music.UID) {
|
||||
|
@ -196,6 +198,7 @@ constructor(
|
|||
/**
|
||||
* Set a new [currentAlbum] from it's [Music.UID]. If the [Music.UID] differs, [currentAlbum]
|
||||
* and [albumList] will be updated to align with the new [Album].
|
||||
*
|
||||
* @param uid The [Music.UID] of the [Album] to update [currentAlbum] to. Must be valid.
|
||||
*/
|
||||
fun setAlbumUid(uid: Music.UID) {
|
||||
|
@ -210,6 +213,7 @@ constructor(
|
|||
/**
|
||||
* Set a new [currentArtist] from it's [Music.UID]. If the [Music.UID] differs, [currentArtist]
|
||||
* and [artistList] will be updated to align with the new [Artist].
|
||||
*
|
||||
* @param uid The [Music.UID] of the [Artist] to update [currentArtist] to. Must be valid.
|
||||
*/
|
||||
fun setArtistUid(uid: Music.UID) {
|
||||
|
@ -224,6 +228,7 @@ constructor(
|
|||
/**
|
||||
* Set a new [currentGenre] from it's [Music.UID]. If the [Music.UID] differs, [currentGenre]
|
||||
* and [genreList] will be updated to align with the new album.
|
||||
*
|
||||
* @param uid The [Music.UID] of the [Genre] to update [currentGenre] to. Must be valid.
|
||||
*/
|
||||
fun setGenreUid(uid: Music.UID) {
|
||||
|
@ -239,6 +244,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Start a new job to load a given [Song]'s [AudioInfo]. Result is pushed to [songAudioInfo].
|
||||
*
|
||||
* @param song The song to load.
|
||||
*/
|
||||
private fun refreshAudioInfo(song: Song) {
|
||||
|
@ -333,6 +339,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* A simpler mapping of [ReleaseType] used for grouping and sorting songs.
|
||||
*
|
||||
* @param headerTitleRes The title string resource to use for a header created out of an
|
||||
* instance of this enum.
|
||||
*/
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows information for a particular [Genre].
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.oxycblt.auxio.util.concatLocalized
|
|||
|
||||
/**
|
||||
* A [ViewBindingDialogFragment] that shows information about a Song.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* An [DetailAdapter] implementing the header and sub-items for the [Album] detail view.
|
||||
*
|
||||
* @param listener A [Listener] to bind interactions to.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -118,6 +119,7 @@ class AlbumDetailAdapter(private val listener: Listener) : DetailAdapter(listene
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays the [Album] header in the detail view. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class AlbumDetailViewHolder private constructor(private val binding: ItemDetailBinding) :
|
||||
|
@ -125,6 +127,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
|||
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param album The new [Album] to bind.
|
||||
* @param listener A [AlbumDetailAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
|
@ -164,6 +167,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -187,12 +191,14 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Disc] to delimit different disc groups. Use [from]
|
||||
* to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class DiscViewHolder(private val binding: ItemDiscHeaderBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param disc The new [disc] to bind.
|
||||
*/
|
||||
fun bind(disc: Disc) {
|
||||
|
@ -209,6 +215,7 @@ private class DiscViewHolder(private val binding: ItemDiscHeaderBinding) :
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -227,12 +234,14 @@ private class DiscViewHolder(private val binding: ItemDiscHeaderBinding) :
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Song] in the context of an [Album]. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class AlbumSongViewHolder private constructor(private val binding: ItemAlbumSongBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param song The new [Song] to bind.
|
||||
* @param listener A [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -276,6 +285,7 @@ private class AlbumSongViewHolder private constructor(private val binding: ItemA
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* A [DetailAdapter] implementing the header and sub-items for the [Artist] detail view.
|
||||
*
|
||||
* @param listener A [DetailAdapter.Listener] to bind interactions to.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -100,6 +101,7 @@ class ArtistDetailAdapter(private val listener: Listener<Music>) :
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays the [Artist] header in the detail view. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class ArtistDetailViewHolder private constructor(private val binding: ItemDetailBinding) :
|
||||
|
@ -107,6 +109,7 @@ private class ArtistDetailViewHolder private constructor(private val binding: It
|
|||
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param artist The new [Artist] to bind.
|
||||
* @param listener A [DetailAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
|
@ -154,6 +157,7 @@ private class ArtistDetailViewHolder private constructor(private val binding: It
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -175,12 +179,14 @@ private class ArtistDetailViewHolder private constructor(private val binding: It
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays an [Album] in the context of an [Artist]. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class ArtistAlbumViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param album The new [Album] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -209,6 +215,7 @@ private class ArtistAlbumViewHolder private constructor(private val binding: Ite
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -227,12 +234,14 @@ private class ArtistAlbumViewHolder private constructor(private val binding: Ite
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Song] in the context of an [Artist]. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class ArtistSongViewHolder private constructor(private val binding: ItemSongBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param song The new [Song] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -258,6 +267,7 @@ private class ArtistSongViewHolder private constructor(private val binding: Item
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] that implements behavior shared across each detail view's adapters.
|
||||
*
|
||||
* @param listener A [Listener] to bind interactions to.
|
||||
* @param diffCallback A [DiffUtil.ItemCallback] to use for item comparison when diffing the
|
||||
* internal list.
|
||||
|
@ -119,6 +120,7 @@ abstract class DetailAdapter(
|
|||
|
||||
/**
|
||||
* A header variation that displays a button to open a sort menu.
|
||||
*
|
||||
* @param titleRes The string resource to use as the header title
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -127,12 +129,14 @@ data class SortHeader(@StringRes override val titleRes: Int) : Header
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [SortHeader], a variation on [BasicHeader] that adds
|
||||
* a button opening a menu for sorting. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class SortHeaderViewHolder(private val binding: ItemSortHeaderBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param sortHeader The new [SortHeader] to bind.
|
||||
* @param listener An [DetailAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
|
@ -152,6 +156,7 @@ private class SortHeaderViewHolder(private val binding: ItemSortHeaderBinding) :
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* An [DetailAdapter] implementing the header and sub-items for the [Genre] detail view.
|
||||
*
|
||||
* @param listener A [DetailAdapter.Listener] to bind interactions to.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -100,12 +101,14 @@ class GenreDetailAdapter(private val listener: Listener<Music>) :
|
|||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays the [Genre] header in the detail view. Use [from] to
|
||||
* create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
private class GenreDetailViewHolder private constructor(private val binding: ItemDetailBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param genre The new [Song] to bind.
|
||||
* @param listener A [DetailAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
|
@ -131,6 +134,7 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* An adapter for [SongProperty] instances.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SongPropertyAdapter :
|
||||
|
@ -48,6 +49,7 @@ class SongPropertyAdapter :
|
|||
|
||||
/**
|
||||
* A property entry for use in [SongPropertyAdapter].
|
||||
*
|
||||
* @param name The contextual title to use for the property.
|
||||
* @param value The value of the property.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
@ -56,6 +58,7 @@ data class SongProperty(@StringRes val name: Int, val value: String) : Item
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [SongProperty]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SongPropertyViewHolder private constructor(private val binding: ItemSongPropertyBinding) :
|
||||
|
@ -69,6 +72,7 @@ class SongPropertyViewHolder private constructor(private val binding: ItemSongPr
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
|
||||
/**
|
||||
* A [FrameLayout] that automatically applies bottom insets.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class EdgeFrameLayout
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.oxycblt.auxio.util.*
|
|||
/**
|
||||
* The starting [SelectionFragment] of Auxio. Shows the user's music library and enables navigation
|
||||
* to other views.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -483,6 +484,7 @@ class HomeFragment :
|
|||
|
||||
/**
|
||||
* [FragmentStateAdapter] implementation for the [HomeFragment]'s [ViewPager2] instance.
|
||||
*
|
||||
* @param tabs The current tab configuration. This will define the [Fragment]s created.
|
||||
* @param fragmentManager The [FragmentManager] required by [FragmentStateAdapter].
|
||||
* @param lifecycleOwner The [LifecycleOwner], whose Lifecycle is required by
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* User configuration specific to the home UI.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface HomeSettings : Settings<HomeSettings.Listener> {
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* The ViewModel for managing the tab data and lists of the home view.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -138,6 +139,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Get the preferred [Sort] for a given [Tab].
|
||||
*
|
||||
* @param tabMode The [MusicMode] of the [Tab] desired.
|
||||
* @return The [Sort] preferred for that [Tab]
|
||||
*/
|
||||
|
@ -151,6 +153,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Update the preferred [Sort] for the current [Tab]. Will update corresponding list.
|
||||
*
|
||||
* @param sort The new [Sort] to apply. Assumed to be an allowed sort for the current [Tab].
|
||||
*/
|
||||
fun setSortForCurrentTab(sort: Sort) {
|
||||
|
@ -178,6 +181,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Update [currentTabMode] to reflect a new ViewPager2 position
|
||||
*
|
||||
* @param pagerPos The new position of the ViewPager2 instance.
|
||||
*/
|
||||
fun synchronizeTabPosition(pagerPos: Int) {
|
||||
|
@ -187,6 +191,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Mark the recreation process as complete.
|
||||
*
|
||||
* @see shouldRecreate
|
||||
*/
|
||||
fun finishRecreate() {
|
||||
|
@ -195,6 +200,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Update whether the user is fast scrolling or not in the home view.
|
||||
*
|
||||
* @param isFastScrolling true if the user is currently fast scrolling, false otherwise.
|
||||
*/
|
||||
fun setFastScrolling(isFastScrolling: Boolean) {
|
||||
|
@ -204,6 +210,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Create a list of [MusicMode]s representing a simpler version of the [Tab] configuration.
|
||||
*
|
||||
* @return A list of the [MusicMode]s for each visible [Tab] in the configuration, ordered in
|
||||
* the same way as the configuration.
|
||||
*/
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.oxycblt.auxio.util.isRtl
|
|||
|
||||
/**
|
||||
* A [MaterialTextView] that displays the popup indicator used in FastScrollRecyclerView
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt), Hai Zhang
|
||||
*/
|
||||
class FastScrollPopupView
|
||||
|
|
|
@ -61,11 +61,10 @@ import org.oxycblt.auxio.util.*
|
|||
* - Added drag listener
|
||||
* - Added documentation
|
||||
*
|
||||
* TODO: Add vibration when popup changes
|
||||
*
|
||||
* TODO: Improve support for variably sized items (Re-back with library fast scroller?)
|
||||
*
|
||||
* @author Hai Zhang, Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Add vibration when popup changes
|
||||
* TODO: Improve support for variably sized items (Re-back with library fast scroller?)
|
||||
*/
|
||||
class FastScrollRecyclerView
|
||||
@JvmOverloads
|
||||
|
@ -508,6 +507,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
interface PopupProvider {
|
||||
/**
|
||||
* Get text to use in the popup at the specified position.
|
||||
*
|
||||
* @param pos The position in the list.
|
||||
* @return A [String] to use in the popup. Null if there is no applicable text for the popup
|
||||
* at [pos].
|
||||
|
@ -519,6 +519,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
interface Listener {
|
||||
/**
|
||||
* Called when the fast scrolling state changes.
|
||||
*
|
||||
* @param isFastScrolling true if the user is currently fast scrolling, false otherwise.
|
||||
*/
|
||||
fun onFastScrollingChanged(isFastScrolling: Boolean)
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.oxycblt.auxio.util.collectImmediately
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows a list of [Album]s.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -154,6 +155,7 @@ class AlbumListFragment :
|
|||
|
||||
/**
|
||||
* A [SelectionIndicatorAdapter] that shows a list of [Album]s using [AlbumViewHolder].
|
||||
*
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class AlbumAdapter(private val listener: SelectableListListener<Album>) :
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.oxycblt.auxio.util.nonZeroOrNull
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows a list of [Artist]s.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -132,6 +133,7 @@ class ArtistListFragment :
|
|||
|
||||
/**
|
||||
* A [SelectionIndicatorAdapter] that shows a list of [Artist]s using [ArtistViewHolder].
|
||||
*
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class ArtistAdapter(private val listener: SelectableListListener<Artist>) :
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.oxycblt.auxio.util.collectImmediately
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows a list of [Genre]s.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -131,6 +132,7 @@ class GenreListFragment :
|
|||
|
||||
/**
|
||||
* A [SelectionIndicatorAdapter] that shows a list of [Genre]s using [GenreViewHolder].
|
||||
*
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class GenreAdapter(private val listener: SelectableListListener<Genre>) :
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.oxycblt.auxio.util.collectImmediately
|
|||
|
||||
/**
|
||||
* A [ListFragment] that shows a list of [Song]s.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -165,6 +166,7 @@ class SongListFragment :
|
|||
|
||||
/**
|
||||
* A [SelectionIndicatorAdapter] that shows a list of [Song]s using [SongViewHolder].
|
||||
*
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
private class SongAdapter(private val listener: SelectableListListener<Song>) :
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.util.logD
|
|||
/**
|
||||
* A [TabLayoutMediator.TabConfigurationStrategy] that uses larger/smaller tab configurations
|
||||
* depending on the screen configuration.
|
||||
*
|
||||
* @param context [Context] required to obtain window information
|
||||
* @param tabs Current tab configuration from settings
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
|
|
@ -22,18 +22,21 @@ import org.oxycblt.auxio.util.logE
|
|||
|
||||
/**
|
||||
* A representation of a library tab suitable for configuration.
|
||||
*
|
||||
* @param mode The type of list in the home view this instance corresponds to.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed class Tab(open val mode: MusicMode) {
|
||||
/**
|
||||
* A visible tab. This will be visible in the home and tab configuration views.
|
||||
*
|
||||
* @param mode The type of list in the home view this instance corresponds to.
|
||||
*/
|
||||
data class Visible(override val mode: MusicMode) : Tab(mode)
|
||||
|
||||
/**
|
||||
* A visible tab. This will be visible in the tab configuration view, but not in the home view.
|
||||
*
|
||||
* @param mode The type of list in the home view this instance corresponds to.
|
||||
*/
|
||||
data class Invisible(override val mode: MusicMode) : Tab(mode)
|
||||
|
@ -68,6 +71,7 @@ sealed class Tab(open val mode: MusicMode) {
|
|||
|
||||
/**
|
||||
* Convert an array of [Tab]s into it's integer representation.
|
||||
*
|
||||
* @param tabs The array of [Tab]s to convert
|
||||
* @return An integer representation of the [Tab] array
|
||||
*/
|
||||
|
@ -93,6 +97,7 @@ sealed class Tab(open val mode: MusicMode) {
|
|||
|
||||
/**
|
||||
* Convert a [Tab] integer representation into it's corresponding array of [Tab]s.
|
||||
*
|
||||
* @param intCode The integer representation of the [Tab]s.
|
||||
* @return An array of [Tab]s corresponding to the sequence.
|
||||
*/
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] that displays an array of [Tab]s open for configuration.
|
||||
*
|
||||
* @param listener A [EditableListListener] for tab interactions.
|
||||
*/
|
||||
class TabAdapter(private val listener: EditableListListener<Tab>) :
|
||||
|
@ -46,6 +47,7 @@ class TabAdapter(private val listener: EditableListListener<Tab>) :
|
|||
|
||||
/**
|
||||
* Immediately update the tab array. This should be used when initializing the list.
|
||||
*
|
||||
* @param newTabs The new array of tabs to show.
|
||||
*/
|
||||
fun submitTabs(newTabs: Array<Tab>) {
|
||||
|
@ -55,6 +57,7 @@ class TabAdapter(private val listener: EditableListListener<Tab>) :
|
|||
|
||||
/**
|
||||
* Update a specific tab to the given value.
|
||||
*
|
||||
* @param at The position of the tab to update.
|
||||
* @param tab The new tab.
|
||||
*/
|
||||
|
@ -66,6 +69,7 @@ class TabAdapter(private val listener: EditableListListener<Tab>) :
|
|||
|
||||
/**
|
||||
* Swap two tabs with each other.
|
||||
*
|
||||
* @param a The position of the first tab to swap.
|
||||
* @param b The position of the second tab to swap.
|
||||
*/
|
||||
|
@ -83,12 +87,14 @@ class TabAdapter(private val listener: EditableListListener<Tab>) :
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Tab]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class TabViewHolder private constructor(private val binding: ItemTabBinding) :
|
||||
DialogRecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param tab The new [Tab] to bind.
|
||||
* @param listener A [EditableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -114,6 +120,7 @@ class TabViewHolder private constructor(private val binding: ItemTabBinding) :
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* A [ViewBindingDialogFragment] that allows the user to modify the home [Tab] configuration.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
/**
|
||||
* An [ItemTouchHelper.Callback] that implements dragging in the [TabAdapter].
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class TabDragCallback(private val adapter: TabAdapter) : ItemTouchHelper.Callback() {
|
||||
|
|
|
@ -55,6 +55,7 @@ constructor(
|
|||
interface Target {
|
||||
/**
|
||||
* Configure the [ImageRequest.Builder] to enable [Target]-specific configuration.
|
||||
*
|
||||
* @param builder The [ImageRequest.Builder] that will be used to request the desired
|
||||
* [Bitmap].
|
||||
* @return The same [ImageRequest.Builder] in order to easily chain configuration methods.
|
||||
|
@ -63,6 +64,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Called when the loading process is completed.
|
||||
*
|
||||
* @param bitmap The loaded bitmap, or null if the bitmap could not be loaded.
|
||||
*/
|
||||
fun onCompleted(bitmap: Bitmap?)
|
||||
|
@ -77,6 +79,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Load the Album cover [Bitmap] from a [Song].
|
||||
*
|
||||
* @param song The song to load a [Bitmap] of it's album cover from.
|
||||
* @param target The [Target] to deliver the [Bitmap] to asynchronously.
|
||||
*/
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.oxycblt.auxio.IntegerTable
|
|||
|
||||
/**
|
||||
* Represents the options available for album cover loading.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
enum class CoverMode {
|
||||
|
@ -33,6 +34,7 @@ enum class CoverMode {
|
|||
|
||||
/**
|
||||
* The integer representation of this instance.
|
||||
*
|
||||
* @see fromIntCode
|
||||
*/
|
||||
val intCode: Int
|
||||
|
@ -46,6 +48,7 @@ enum class CoverMode {
|
|||
companion object {
|
||||
/**
|
||||
* Convert a [CoverMode] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [CoverMode]
|
||||
* @return The corresponding [CoverMode], or null if the [CoverMode] is invalid.
|
||||
* @see CoverMode.intCode
|
||||
|
|
|
@ -48,9 +48,9 @@ import org.oxycblt.auxio.util.getInteger
|
|||
* This class is primarily intended for list items. For other uses, [StyledImageView] is more
|
||||
* suitable.
|
||||
*
|
||||
* TODO: Rework content descriptions here
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Rework content descriptions here
|
||||
*/
|
||||
class ImageGroup
|
||||
@JvmOverloads
|
||||
|
@ -146,6 +146,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Bind a [Song] to the internal [StyledImageView].
|
||||
*
|
||||
* @param song The [Song] to bind to the view.
|
||||
* @see StyledImageView.bind
|
||||
*/
|
||||
|
@ -153,6 +154,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Bind a [Album] to the internal [StyledImageView].
|
||||
*
|
||||
* @param album The [Album] to bind to the view.
|
||||
* @see StyledImageView.bind
|
||||
*/
|
||||
|
@ -160,6 +162,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Bind a [Genre] to the internal [StyledImageView].
|
||||
*
|
||||
* @param artist The [Artist] to bind to the view.
|
||||
* @see StyledImageView.bind
|
||||
*/
|
||||
|
@ -167,6 +170,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Bind a [Genre] to the internal [StyledImageView].
|
||||
*
|
||||
* @param genre The [Genre] to bind to the view.
|
||||
* @see StyledImageView.bind
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* User configuration specific to image loading.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface ImageSettings : Settings<ImageSettings.Listener> {
|
||||
|
|
|
@ -48,7 +48,6 @@ import org.oxycblt.auxio.util.getDrawableCompat
|
|||
|
||||
/**
|
||||
* An [AppCompatImageView] with some additional styling, including:
|
||||
*
|
||||
* - Tonal background
|
||||
* - Rounded corners based on user preferences
|
||||
* - Built-in support for binding image data or using a static icon with the same styling as
|
||||
|
@ -97,30 +96,35 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Bind a [Song]'s album cover to this view, also updating the content description.
|
||||
*
|
||||
* @param song The [Song] to bind.
|
||||
*/
|
||||
fun bind(song: Song) = bindImpl(song, R.drawable.ic_song_24, R.string.desc_album_cover)
|
||||
|
||||
/**
|
||||
* Bind an [Album]'s cover to this view, also updating the content description.
|
||||
*
|
||||
* @param album the [Album] to bind.
|
||||
*/
|
||||
fun bind(album: Album) = bindImpl(album, R.drawable.ic_album_24, R.string.desc_album_cover)
|
||||
|
||||
/**
|
||||
* Bind an [Artist]'s image to this view, also updating the content description.
|
||||
*
|
||||
* @param artist the [Artist] to bind.
|
||||
*/
|
||||
fun bind(artist: Artist) = bindImpl(artist, R.drawable.ic_artist_24, R.string.desc_artist_image)
|
||||
|
||||
/**
|
||||
* Bind an [Genre]'s image to this view, also updating the content description.
|
||||
*
|
||||
* @param genre the [Genre] to bind.
|
||||
*/
|
||||
fun bind(genre: Genre) = bindImpl(genre, R.drawable.ic_genre_24, R.string.desc_genre_image)
|
||||
|
||||
/**
|
||||
* Internally bind a [Music]'s image to this view.
|
||||
*
|
||||
* @param music The music to find.
|
||||
* @param errorRes The error drawable resource to use if the music cannot be loaded.
|
||||
* @param descRes The content description string resource to use. The resource must have one
|
||||
|
@ -144,6 +148,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
/**
|
||||
* A [Drawable] wrapper that re-styles the drawable to better align with the style of
|
||||
* [StyledImageView].
|
||||
*
|
||||
* @param context [Context] required for initialization.
|
||||
* @param inner The [Drawable] to wrap.
|
||||
*/
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.oxycblt.auxio.music.Song
|
|||
|
||||
/**
|
||||
* A [Keyer] implementation for [Music] data.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class MusicKeyer : Keyer<Music> {
|
||||
|
@ -56,6 +57,7 @@ class MusicKeyer : Keyer<Music> {
|
|||
/**
|
||||
* Generic [Fetcher] for [Album] covers. Works with both [Album] and [Song]. Use [SongFactory] or
|
||||
* [AlbumFactory] for instantiation.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class AlbumCoverFetcher
|
||||
|
@ -89,6 +91,7 @@ private constructor(
|
|||
|
||||
/**
|
||||
* [Fetcher] for [Artist] images. Use [Factory] for instantiation.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ArtistImageFetcher
|
||||
|
@ -116,6 +119,7 @@ private constructor(
|
|||
|
||||
/**
|
||||
* [Fetcher] for [Genre] images. Use [Factory] for instantiation.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class GenreImageFetcher
|
||||
|
@ -141,6 +145,7 @@ private constructor(
|
|||
/**
|
||||
* Map at most N [T] items a collection into a collection of [R], ignoring [T] that cannot be
|
||||
* transformed into [R].
|
||||
*
|
||||
* @param n The maximum amount of items to map.
|
||||
* @param transform The function that transforms data [T] from the original list into data [R] in
|
||||
* the new list. Can return null if the [T] cannot be transformed into an [R].
|
||||
|
|
|
@ -38,11 +38,13 @@ import org.oxycblt.auxio.util.logW
|
|||
|
||||
/**
|
||||
* Internal utilities for loading album covers.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt).
|
||||
*/
|
||||
object Covers {
|
||||
/**
|
||||
* Fetch an album cover, respecting the current cover configuration.
|
||||
*
|
||||
* @param context [Context] required to load the image.
|
||||
* @param imageSettings [ImageSettings] required to obtain configuration information.
|
||||
* @param album [Album] to load the cover from.
|
||||
|
@ -80,6 +82,7 @@ object Covers {
|
|||
|
||||
/**
|
||||
* Loads an album cover with [MediaMetadataRetriever].
|
||||
*
|
||||
* @param context [Context] required to load the image.
|
||||
* @param album [Album] to load the cover from.
|
||||
* @return An [InputStream] of image data if the cover loading was successful, null otherwise.
|
||||
|
@ -99,6 +102,7 @@ object Covers {
|
|||
|
||||
/**
|
||||
* Loads an [Album] cover with ExoPlayer's [MetadataRetriever].
|
||||
*
|
||||
* @param context [Context] required to load the image.
|
||||
* @param album [Album] to load the cover from.
|
||||
* @return An [InputStream] of image data if the cover loading was successful, null otherwise.
|
||||
|
@ -173,6 +177,7 @@ object Covers {
|
|||
|
||||
/**
|
||||
* Loads an [Album] cover from MediaStore.
|
||||
*
|
||||
* @param context [Context] required to load the image.
|
||||
* @param album [Album] to load the cover from.
|
||||
* @return An [InputStream] of image data if the cover loading was successful, null otherwise.
|
||||
|
|
|
@ -27,6 +27,7 @@ import coil.transition.TransitionTarget
|
|||
|
||||
/**
|
||||
* A copy of [CrossfadeTransition.Factory] that also applies a transition to error results.
|
||||
*
|
||||
* @author Coil Team, Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ErrorCrossfadeTransitionFactory : Transition.Factory {
|
||||
|
|
|
@ -37,12 +37,14 @@ import okio.source
|
|||
|
||||
/**
|
||||
* Utilities for constructing Artist and Genre images.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt), Karim Abou Zeid
|
||||
*/
|
||||
object Images {
|
||||
/**
|
||||
* Create a mosaic image from the given image [InputStream]s. Derived from phonograph:
|
||||
* https://github.com/kabouzeid/Phonograph
|
||||
*
|
||||
* @param context [Context] required to generate the mosaic.
|
||||
* @param streams [InputStream]s of image data to create the mosaic out of.
|
||||
* @param size [Size] of the Mosaic to generate.
|
||||
|
@ -104,6 +106,7 @@ object Images {
|
|||
|
||||
/**
|
||||
* Get an image dimension suitable to create a mosaic with.
|
||||
*
|
||||
* @return A pixel dimension derived from the given [Dimension] that will always be even,
|
||||
* allowing it to be sub-divided.
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,7 @@ import kotlin.math.min
|
|||
/**
|
||||
* A transformation that performs a center crop-style transformation on an image. Allowing this
|
||||
* behavior to be intrinsic without any view configuration.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SquareFrameTransform : Transformation {
|
||||
|
|
|
@ -24,6 +24,7 @@ interface Item
|
|||
|
||||
/**
|
||||
* A "header" used for delimiting groups of data.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Header : Item {
|
||||
|
@ -33,6 +34,7 @@ interface Header : Item {
|
|||
|
||||
/**
|
||||
* A basic header with no additional actions.
|
||||
*
|
||||
* @param titleRes The string resource used for the header's title.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
|
||||
/**
|
||||
* A Fragment containing a selectable list.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
||||
|
@ -52,6 +53,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Called when [onClick] is called, but does not result in the item being selected. This more or
|
||||
* less corresponds to an [onClick] implementation in a non-[ListFragment].
|
||||
*
|
||||
* @param item The [T] data of the item that was clicked.
|
||||
*/
|
||||
abstract fun onRealClick(item: T)
|
||||
|
@ -73,6 +75,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Opens a menu in the context of a [Song]. This menu will be managed by the Fragment and closed
|
||||
* when the view is destroyed. If a menu is already opened, this call is ignored.
|
||||
*
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
* @param menuRes The resource of the menu to load.
|
||||
* @param song The [Song] to create the menu for.
|
||||
|
@ -111,6 +114,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Opens a menu in the context of a [Album]. This menu will be managed by the Fragment and
|
||||
* closed when the view is destroyed. If a menu is already opened, this call is ignored.
|
||||
*
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
* @param menuRes The resource of the menu to load.
|
||||
* @param album The [Album] to create the menu for.
|
||||
|
@ -147,6 +151,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Opens a menu in the context of a [Artist]. This menu will be managed by the Fragment and
|
||||
* closed when the view is destroyed. If a menu is already opened, this call is ignored.
|
||||
*
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
* @param menuRes The resource of the menu to load.
|
||||
* @param artist The [Artist] to create the menu for.
|
||||
|
@ -180,6 +185,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Opens a menu in the context of a [Genre]. This menu will be managed by the Fragment and
|
||||
* closed when the view is destroyed. If a menu is already opened, this call is ignored.
|
||||
*
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
* @param menuRes The resource of the menu to load.
|
||||
* @param genre The [Genre] to create the menu for.
|
||||
|
@ -226,6 +232,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
/**
|
||||
* Open a menu. This menu will be managed by the Fragment and closed when the view is destroyed.
|
||||
* If a menu is already opened, this call is ignored.
|
||||
*
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
* @param menuRes The resource of the menu to load.
|
||||
* @param block A block that is ran within [PopupMenu] that allows further configuration.
|
||||
|
|
|
@ -23,11 +23,13 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
/**
|
||||
* A basic listener for list interactions.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface ClickableListListener<in T> {
|
||||
/**
|
||||
* Called when an item in the list is clicked.
|
||||
*
|
||||
* @param item The [T] item that was clicked.
|
||||
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
|
||||
*/
|
||||
|
@ -35,6 +37,7 @@ interface ClickableListListener<in T> {
|
|||
|
||||
/**
|
||||
* Binds this instance to a list item.
|
||||
*
|
||||
* @param item The [T] to bind this item to.
|
||||
* @param viewHolder The [RecyclerView.ViewHolder] of the item that was clicked.
|
||||
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||
|
@ -47,17 +50,20 @@ interface ClickableListListener<in T> {
|
|||
|
||||
/**
|
||||
* An extension of [ClickableListListener] that enables list editing functionality.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface EditableListListener<in T> : ClickableListListener<T> {
|
||||
/**
|
||||
* Called when a [RecyclerView.ViewHolder] requests that it should be dragged.
|
||||
*
|
||||
* @param viewHolder The [RecyclerView.ViewHolder] that should start being dragged.
|
||||
*/
|
||||
fun onPickUp(viewHolder: RecyclerView.ViewHolder)
|
||||
|
||||
/**
|
||||
* Binds this instance to a list item.
|
||||
*
|
||||
* @param item The [T] to bind this item to.
|
||||
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
|
||||
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||
|
@ -83,11 +89,13 @@ interface EditableListListener<in T> : ClickableListListener<T> {
|
|||
|
||||
/**
|
||||
* An extension of [ClickableListListener] that enables menu and selection functionality.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface SelectableListListener<in T> : ClickableListListener<T> {
|
||||
/**
|
||||
* Called when an item in the list requests that a menu related to it should be opened.
|
||||
*
|
||||
* @param item The [T] item to open a menu for.
|
||||
* @param anchor The [View] to anchor the menu to.
|
||||
*/
|
||||
|
@ -95,12 +103,14 @@ interface SelectableListListener<in T> : ClickableListListener<T> {
|
|||
|
||||
/**
|
||||
* Called when an item in the list requests that it be selected.
|
||||
*
|
||||
* @param item The [T] item to select.
|
||||
*/
|
||||
fun onSelect(item: T)
|
||||
|
||||
/**
|
||||
* Binds this instance to a list item.
|
||||
*
|
||||
* @param item The [T] to bind this item to.
|
||||
* @param viewHolder The [RecyclerView.ViewHolder] to bind.
|
||||
* @param bodyView The [View] containing the main body of the list item. Any click events on
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.oxycblt.auxio.music.metadata.Disc
|
|||
data class Sort(val mode: Mode, val direction: Direction) {
|
||||
/**
|
||||
* Create a new [Sort] with the same [mode], but a different [Direction].
|
||||
*
|
||||
* @param direction The new [Direction] to sort in.
|
||||
* @return A new sort with the same mode, but with the new [Direction] value applied.
|
||||
*/
|
||||
|
@ -45,6 +46,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Create a new [Sort] with the same [direction] value, but different [mode] value.
|
||||
*
|
||||
* @param mode Tbe new mode to use for the Sort.
|
||||
* @return A new sort with the same [direction] value, but with the new [mode] applied.
|
||||
*/
|
||||
|
@ -52,6 +54,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a list of [Song]s.
|
||||
*
|
||||
* @param songs The list of [Song]s.
|
||||
* @return A new list of [Song]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
|
@ -63,6 +66,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a list of [Album]s.
|
||||
*
|
||||
* @param albums The list of [Album]s.
|
||||
* @return A new list of [Album]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
|
@ -74,6 +78,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a list of [Artist]s.
|
||||
*
|
||||
* @param artists The list of [Artist]s.
|
||||
* @return A new list of [Artist]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
|
@ -85,6 +90,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a list of [Genre]s.
|
||||
*
|
||||
* @param genres The list of [Genre]s.
|
||||
* @return A new list of [Genre]s sorted by this [Sort]'s configuration.
|
||||
*/
|
||||
|
@ -96,6 +102,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a *mutable* list of [Song]s in-place using this [Sort]'s configuration.
|
||||
*
|
||||
* @param songs The [Song]s to sort.
|
||||
*/
|
||||
private fun songsInPlace(songs: MutableList<out Song>) {
|
||||
|
@ -104,6 +111,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a *mutable* list of [Album]s in-place using this [Sort]'s configuration.
|
||||
*
|
||||
* @param albums The [Album]s to sort.
|
||||
*/
|
||||
private fun albumsInPlace(albums: MutableList<out Album>) {
|
||||
|
@ -112,6 +120,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a *mutable* list of [Artist]s in-place using this [Sort]'s configuration.
|
||||
*
|
||||
* @param artists The [Album]s to sort.
|
||||
*/
|
||||
private fun artistsInPlace(artists: MutableList<out Artist>) {
|
||||
|
@ -120,6 +129,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort a *mutable* list of [Genre]s in-place using this [Sort]'s configuration.
|
||||
*
|
||||
* @param genres The [Genre]s to sort.
|
||||
*/
|
||||
private fun genresInPlace(genres: MutableList<out Genre>) {
|
||||
|
@ -128,6 +138,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* The integer representation of this instance.
|
||||
*
|
||||
* @see fromIntCode
|
||||
*/
|
||||
val intCode: Int
|
||||
|
@ -150,6 +161,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Get a [Comparator] that sorts [Song]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Song] list according to this [Mode].
|
||||
*/
|
||||
|
@ -159,6 +171,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Get a [Comparator] that sorts [Album]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Album] list according to this [Mode].
|
||||
*/
|
||||
|
@ -168,6 +181,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Return a [Comparator] that sorts [Artist]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Artist] list according to this [Mode].
|
||||
*/
|
||||
|
@ -177,6 +191,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Return a [Comparator] that sorts [Genre]s according to this [Mode].
|
||||
*
|
||||
* @param direction The direction to sort in.
|
||||
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
|
||||
*/
|
||||
|
@ -186,6 +201,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the item's name.
|
||||
*
|
||||
* @see Music.collationKey
|
||||
*/
|
||||
object ByName : Mode() {
|
||||
|
@ -210,6 +226,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the [Album] of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Album.collationKey
|
||||
*/
|
||||
object ByAlbum : Mode() {
|
||||
|
@ -229,6 +246,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the [Artist] name of an item. Only available for [Song] and [Album].
|
||||
*
|
||||
* @see Artist.collationKey
|
||||
*/
|
||||
object ByArtist : Mode() {
|
||||
|
@ -256,6 +274,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the [Date] of an item. Only available for [Song] and [Album].
|
||||
*
|
||||
* @see Song.date
|
||||
* @see Album.dates
|
||||
*/
|
||||
|
@ -308,6 +327,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the amount of songs an item contains. Only available for [MusicParent]s.
|
||||
*
|
||||
* @see MusicParent.songs
|
||||
*/
|
||||
object ByCount : Mode() {
|
||||
|
@ -333,6 +353,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the disc number of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Song.disc
|
||||
*/
|
||||
object ByDisc : Mode() {
|
||||
|
@ -351,6 +372,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the track number of an item. Only available for [Song]s.
|
||||
*
|
||||
* @see Song.track
|
||||
*/
|
||||
object ByTrack : Mode() {
|
||||
|
@ -369,6 +391,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Sort by the date an item was added. Only supported by [Song]s and [Album]s.
|
||||
*
|
||||
* @see Song.dateAdded
|
||||
* @see Album.dates
|
||||
*/
|
||||
|
@ -391,6 +414,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Utility function to create a [Comparator] in a dynamic way determined by [direction].
|
||||
*
|
||||
* @param direction The [Direction] to sort in.
|
||||
* @see compareBy
|
||||
* @see compareByDescending
|
||||
|
@ -406,6 +430,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Utility function to create a [Comparator] in a dynamic way determined by [direction]
|
||||
*
|
||||
* @param direction The [Direction] to sort in.
|
||||
* @param comparator A [Comparator] to wrap.
|
||||
* @return A new [Comparator] with the specified configuration.
|
||||
|
@ -419,6 +444,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Utility function to create a [Comparator] a dynamic way determined by [direction]
|
||||
*
|
||||
* @param direction The [Direction] to sort in.
|
||||
* @param comparator A [Comparator] to wrap.
|
||||
* @param selector Called to obtain a specific attribute to sort by.
|
||||
|
@ -439,6 +465,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
/**
|
||||
* Utility function to create a [Comparator] that sorts in ascending order based on the
|
||||
* given [Comparator], with a selector based on the item itself.
|
||||
*
|
||||
* @param comparator The [Comparator] to wrap.
|
||||
* @return A new [Comparator] with the specified configuration.
|
||||
* @see compareBy
|
||||
|
@ -448,6 +475,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* A [Comparator] that chains several other [Comparator]s together to form one comparison.
|
||||
*
|
||||
* @param comparators The [Comparator]s to chain. These will be iterated through in order
|
||||
* during a comparison, with the first non-equal result becoming the result.
|
||||
*/
|
||||
|
@ -468,6 +496,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Wraps a [Comparator], extending it to compare two lists.
|
||||
*
|
||||
* @param inner The [Comparator] to use.
|
||||
*/
|
||||
private class ListComparator<T>(private val inner: Comparator<T>) : Comparator<List<T>> {
|
||||
|
@ -500,6 +529,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
/**
|
||||
* A [Comparator] that compares abstract [Music] values. Internally, this is similar to
|
||||
* [NullableComparator], however comparing [Music.collationKey] instead of [Comparable].
|
||||
*
|
||||
* @see NullableComparator
|
||||
* @see Music.collationKey
|
||||
*/
|
||||
|
@ -555,6 +585,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
companion object {
|
||||
/**
|
||||
* Convert a [Mode] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [Mode]
|
||||
* @return The corresponding [Mode], or null if the [Mode] is invalid.
|
||||
* @see intCode
|
||||
|
@ -575,6 +606,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
|
||||
/**
|
||||
* Convert a menu item ID into a [Mode].
|
||||
*
|
||||
* @param itemId The menu resource ID to convert
|
||||
* @return A [Mode] corresponding to the given ID, or null if the ID is invalid.
|
||||
* @see itemId
|
||||
|
@ -604,6 +636,7 @@ data class Sort(val mode: Mode, val direction: Direction) {
|
|||
companion object {
|
||||
/**
|
||||
* Convert a [Sort] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [Sort]
|
||||
* @return The corresponding [Sort], or null if the [Sort] is invalid.
|
||||
* @see intCode
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] with [ListDiffer] integration.
|
||||
*
|
||||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
*/
|
||||
abstract class DiffAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
||||
|
@ -36,6 +37,7 @@ abstract class DiffAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
|
||||
/**
|
||||
* Get a [T] item at the given position.
|
||||
*
|
||||
* @param at The position to get the item at.
|
||||
* @throws IndexOutOfBoundsException If the index is not in the list bounds/
|
||||
*/
|
||||
|
@ -43,6 +45,7 @@ abstract class DiffAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
|
||||
/**
|
||||
* Dynamically determine how to update the list based on the given instructions.
|
||||
*
|
||||
* @param newList The new list of [T] items to show.
|
||||
* @param instructions The instructions specifying how to update the list.
|
||||
* @param onDone Called when the update process is completed. Defaults to a no-op.
|
||||
|
|
|
@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
|
||||
/**
|
||||
* List differ wrapper that provides more flexibility regarding the way lists are updated.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface ListDiffer<T, I> {
|
||||
|
@ -36,6 +37,7 @@ interface ListDiffer<T, I> {
|
|||
|
||||
/**
|
||||
* Dynamically determine how to update the list based on the given instructions.
|
||||
*
|
||||
* @param newList The new list of [T] items to show.
|
||||
* @param instructions The [BasicListInstructions] specifying how to update the list.
|
||||
* @param onDone Called when the update process is completed.
|
||||
|
@ -49,6 +51,7 @@ interface ListDiffer<T, I> {
|
|||
abstract class Factory<T, I> {
|
||||
/**
|
||||
* Create a new [ListDiffer] bound to the given [RecyclerView.Adapter].
|
||||
*
|
||||
* @param adapter The [RecyclerView.Adapter] to bind to.
|
||||
*/
|
||||
abstract fun new(adapter: RecyclerView.Adapter<*>): ListDiffer<T, I>
|
||||
|
@ -57,6 +60,7 @@ interface ListDiffer<T, I> {
|
|||
/**
|
||||
* Update lists on another thread. This is useful when large diffs are likely to occur in this
|
||||
* list that would be exceedingly slow with [Blocking].
|
||||
*
|
||||
* @param diffCallback A [DiffUtil.ItemCallback] to use for item comparison when diffing the
|
||||
* internal list.
|
||||
*/
|
||||
|
@ -69,6 +73,7 @@ interface ListDiffer<T, I> {
|
|||
/**
|
||||
* Update lists on the main thread. This is useful when many small, discrete list diffs are
|
||||
* likely to occur that would cause [Async] to suffer from race conditions.
|
||||
*
|
||||
* @param diffCallback A [DiffUtil.ItemCallback] to use for item comparison when diffing the
|
||||
* internal list.
|
||||
*/
|
||||
|
@ -81,6 +86,7 @@ interface ListDiffer<T, I> {
|
|||
|
||||
/**
|
||||
* Represents the specific way to update a list of items.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
enum class BasicListInstructions {
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* A [RecyclerView.Adapter] that supports indicating the playback status of a particular item.
|
||||
*
|
||||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -50,6 +51,7 @@ abstract class PlayingIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
}
|
||||
/**
|
||||
* Update the currently playing item in the list.
|
||||
*
|
||||
* @param item The [T] currently being played, or null if it is not being played.
|
||||
* @param isPlaying Whether playback is ongoing or paused.
|
||||
*/
|
||||
|
@ -103,6 +105,7 @@ abstract class PlayingIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
abstract class ViewHolder(root: View) : RecyclerView.ViewHolder(root) {
|
||||
/**
|
||||
* Update the playing indicator within this [RecyclerView.ViewHolder].
|
||||
*
|
||||
* @param isActive True if this item is playing, false otherwise.
|
||||
* @param isPlaying True if playback is ongoing, false if paused. If this is true,
|
||||
* [isActive] will also be true.
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.music.Music
|
|||
/**
|
||||
* A [PlayingIndicatorAdapter] that also supports indicating the selection status of a group of
|
||||
* items.
|
||||
*
|
||||
* @param differFactory The [ListDiffer.Factory] that defines the type of [ListDiffer] to use.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -41,6 +42,7 @@ abstract class SelectionIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
|
||||
/**
|
||||
* Update the list of selected items.
|
||||
*
|
||||
* @param items A set of selected [T] items.
|
||||
*/
|
||||
fun setSelected(items: Set<T>) {
|
||||
|
@ -74,6 +76,7 @@ abstract class SelectionIndicatorAdapter<T, I, VH : RecyclerView.ViewHolder>(
|
|||
abstract class ViewHolder(root: View) : PlayingIndicatorAdapter.ViewHolder(root) {
|
||||
/**
|
||||
* Update the selection indicator within this [PlayingIndicatorAdapter.ViewHolder].
|
||||
*
|
||||
* @param isSelected Whether this [PlayingIndicatorAdapter.ViewHolder] is selected.
|
||||
*/
|
||||
abstract fun updateSelectionIndicator(isSelected: Boolean)
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.oxycblt.auxio.list.Item
|
|||
/**
|
||||
* A [DiffUtil.ItemCallback] that automatically implements the [areItemsTheSame] method. Use this
|
||||
* whenever creating [DiffUtil.ItemCallback] implementations with an [Item] subclass.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
abstract class SimpleDiffCallback<T : Item> : DiffUtil.ItemCallback<T>() {
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
* - Automatic edge-to-edge support
|
||||
* - Adapter-based [SpanSizeLookup] implementation
|
||||
* - Automatic [setHasFixedSize] setup
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
open class AuxioRecyclerView
|
||||
|
@ -89,6 +90,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
interface SpanSizeLookup {
|
||||
/**
|
||||
* Get if the item at a position takes up the whole width of the [RecyclerView] or not.
|
||||
*
|
||||
* @param position The position of the item.
|
||||
* @return true if the item is full-width, false otherwise.
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.oxycblt.auxio.util.getDimenPixels
|
|||
* A [RecyclerView] intended for use in Dialogs, adding features such as:
|
||||
* - NestedScrollView scrollIndicators behavior emulation
|
||||
* - Dialog-specific [ViewHolder] that automatically resolves certain issues.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class DialogRecyclerView
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.oxycblt.auxio.list.adapter.DiffAdapter
|
|||
/**
|
||||
* A [BackportMaterialDividerItemDecoration] that sets up the divider configuration to correctly
|
||||
* separate content with headers.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class HeaderItemDecoration
|
||||
|
|
|
@ -36,12 +36,14 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Song]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SongViewHolder private constructor(private val binding: ItemSongBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param song The new [Song] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -67,6 +69,7 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) :
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -84,12 +87,14 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) :
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Album]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class AlbumViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param album The new [Album] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -115,6 +120,7 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -133,12 +139,14 @@ class AlbumViewHolder private constructor(private val binding: ItemParentBinding
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Artist]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ArtistViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param artist The new [Artist] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -173,6 +181,7 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -192,12 +201,14 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [Genre]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class GenreViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
SelectionIndicatorAdapter.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param genre The new [Genre] to bind.
|
||||
* @param listener An [SelectableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -227,6 +238,7 @@ class GenreViewHolder private constructor(private val binding: ItemParentBinding
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
@ -243,12 +255,14 @@ class GenreViewHolder private constructor(private val binding: ItemParentBinding
|
|||
|
||||
/**
|
||||
* A [RecyclerView.ViewHolder] that displays a [BasicHeader]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class BasicHeaderViewHolder private constructor(private val binding: ItemHeaderBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param basicHeader The new [BasicHeader] to bind.
|
||||
*/
|
||||
fun bind(basicHeader: BasicHeader) {
|
||||
|
@ -262,6 +276,7 @@ class BasicHeaderViewHolder private constructor(private val binding: ItemHeaderB
|
|||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
|
||||
/**
|
||||
* A subset of ListFragment that implements aspects of the selection UI.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
abstract class SelectionFragment<VB : ViewBinding> :
|
||||
|
@ -38,6 +39,7 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
|||
/**
|
||||
* Get the [SelectionToolbarOverlay] of the concrete Fragment to be automatically managed by
|
||||
* [SelectionFragment].
|
||||
*
|
||||
* @return The [SelectionToolbarOverlay] of the concrete [SelectionFragment]'s [VB], or null if
|
||||
* there is not one.
|
||||
*/
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.oxycblt.auxio.util.logD
|
|||
/**
|
||||
* A wrapper around a [MaterialToolbar] that adds an additional [MaterialToolbar] showing the
|
||||
* current selection state.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class SelectionToolbarOverlay
|
||||
|
@ -65,6 +66,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
/**
|
||||
* Set an OnClickListener for when the "cancel" button in the selection [MaterialToolbar] is
|
||||
* pressed.
|
||||
*
|
||||
* @param listener The OnClickListener to respond to this interaction.
|
||||
* @see MaterialToolbar.setNavigationOnClickListener
|
||||
*/
|
||||
|
@ -75,6 +77,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
/**
|
||||
* Set an [OnMenuItemClickListener] for when a MenuItem is selected from the selection
|
||||
* [MaterialToolbar].
|
||||
*
|
||||
* @param listener The [OnMenuItemClickListener] to respond to this interaction.
|
||||
* @see MaterialToolbar.setOnMenuItemClickListener
|
||||
*/
|
||||
|
@ -84,6 +87,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Update the selection [MaterialToolbar] to reflect the current selection amount.
|
||||
*
|
||||
* @param amount The amount of items that are currently selected.
|
||||
* @return true if the selection [MaterialToolbar] changes, false otherwise.
|
||||
*/
|
||||
|
@ -101,6 +105,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Animate the visibility of the inner and selection [MaterialToolbar]s to the given state.
|
||||
*
|
||||
* @param selectionVisible Whether the selection [MaterialToolbar] should be visible or not.
|
||||
* @return true if the toolbars have changed, false otherwise.
|
||||
*/
|
||||
|
@ -152,6 +157,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
|
||||
/**
|
||||
* Update the alpha of the inner and selection [MaterialToolbar]s.
|
||||
*
|
||||
* @param innerAlpha The opacity of the inner [MaterialToolbar]. This will map to the inverse
|
||||
* opacity of the selection [MaterialToolbar].
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.music.model.Library
|
|||
|
||||
/**
|
||||
* A [ViewModel] that manages the current selection.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -67,6 +68,7 @@ class SelectionViewModel @Inject constructor(private val musicRepository: MusicR
|
|||
/**
|
||||
* Select a new [Music] item. If this item is already within the selected items, the item will
|
||||
* be removed. Otherwise, it will be added.
|
||||
*
|
||||
* @param music The [Music] item to select.
|
||||
*/
|
||||
fun select(music: Music) {
|
||||
|
@ -79,6 +81,7 @@ class SelectionViewModel @Inject constructor(private val musicRepository: MusicR
|
|||
|
||||
/**
|
||||
* Consume the current selection. This will clear any items that were selected prior.
|
||||
*
|
||||
* @return The list of selected items before it was cleared.
|
||||
*/
|
||||
fun consume() = _selected.value.also { _selected.value = listOf() }
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.google.android.exoplayer2.extractor.wav.WavExtractor
|
|||
|
||||
/**
|
||||
* A [ExtractorsFactory] that only provides audio containers to save APK space.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
object AudioOnlyExtractors : ExtractorsFactory {
|
||||
|
|
|
@ -38,11 +38,13 @@ import org.oxycblt.auxio.util.toUuidOrNull
|
|||
/**
|
||||
* Abstract music data. This contains universal information about all concrete music
|
||||
* implementations, such as identification information and names.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed interface Music : Item {
|
||||
/**
|
||||
* A unique identifier for this music item.
|
||||
*
|
||||
* @see UID
|
||||
*/
|
||||
val uid: UID
|
||||
|
@ -56,6 +58,7 @@ sealed interface Music : Item {
|
|||
/**
|
||||
* Returns a name suitable for use in the app UI. This should be favored over [rawName] in
|
||||
* nearly all cases.
|
||||
*
|
||||
* @param context [Context] required to obtain placeholder text or formatting information.
|
||||
* @return A human-readable string representing the name of this music. In the case that the
|
||||
* item does not have a name, an analogous "Unknown X" name is returned.
|
||||
|
@ -86,10 +89,9 @@ sealed interface Music : Item {
|
|||
* [UID] enables a much cheaper and more reliable form of differentiating music, derived from
|
||||
* either a hash of meaningful metadata or the MusicBrainz ID spec. Using this enables several
|
||||
* improvements to music management in this app, including:
|
||||
*
|
||||
* - Proper differentiation of identical music. It's common for large, well-tagged libraries to
|
||||
* have functionally duplicate items that are differentiated with MusicBrainz IDs, and so [UID]
|
||||
* allows us to properly differentiate between these in the app.
|
||||
* have functionally duplicate items that are differentiated with MusicBrainz IDs, and so
|
||||
* [UID] allows us to properly differentiate between these in the app.
|
||||
* - Better music persistence between restarts. Whereas directly storing song names would be
|
||||
* prone to collisions, and storing MediaStore IDs would drift rapidly as the music library
|
||||
* changes, [UID] enables a much stronger form of persistence given it's unique link to a
|
||||
|
@ -125,6 +127,7 @@ sealed interface Music : Item {
|
|||
|
||||
/**
|
||||
* Internal marker of [Music.UID] format type.
|
||||
*
|
||||
* @param namespace Namespace to use in the [Music.UID]'s string representation.
|
||||
*/
|
||||
private enum class Format(val namespace: String) {
|
||||
|
@ -139,6 +142,7 @@ sealed interface Music : Item {
|
|||
/**
|
||||
* Creates an Auxio-style [UID] with a [UUID] composed of a hash of the non-subjective,
|
||||
* unlikely-to-change metadata of the music.
|
||||
*
|
||||
* @param mode The analogous [MusicMode] of the item that created this [UID].
|
||||
* @param updates Block to update the [MessageDigest] hash with the metadata of the
|
||||
* item. Make sure the metadata hashed semantically aligns with the format
|
||||
|
@ -181,6 +185,7 @@ sealed interface Music : Item {
|
|||
/**
|
||||
* Creates a MusicBrainz-style [UID] with a [UUID] derived from the MusicBrainz ID
|
||||
* extracted from a file.
|
||||
*
|
||||
* @param mode The analogous [MusicMode] of the item that created this [UID].
|
||||
* @param mbid The analogous MusicBrainz ID for this item that was extracted from a
|
||||
* file.
|
||||
|
@ -190,6 +195,7 @@ sealed interface Music : Item {
|
|||
|
||||
/**
|
||||
* Convert a [UID]'s string representation back into a concrete [UID] instance.
|
||||
*
|
||||
* @param uid The [UID]'s string representation, formatted as
|
||||
* `format_namespace:music_mode_int-uuid`.
|
||||
* @return A [UID] converted from the string representation, or null if the string
|
||||
|
@ -224,6 +230,7 @@ sealed interface Music : Item {
|
|||
|
||||
/**
|
||||
* An abstract grouping of [Song]s and other [Music] data.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed interface MusicParent : Music {
|
||||
|
@ -233,6 +240,7 @@ sealed interface MusicParent : Music {
|
|||
|
||||
/**
|
||||
* A song.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Song : Music {
|
||||
|
@ -281,6 +289,7 @@ interface Song : Music {
|
|||
/**
|
||||
* An abstract release group. While it may be called an album, it encompasses other types of
|
||||
* releases like singles, EPs, and compilations.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Album : MusicParent {
|
||||
|
@ -311,6 +320,7 @@ interface Album : MusicParent {
|
|||
/**
|
||||
* An abstract artist. These are actually a combination of the artist and album artist tags from
|
||||
* within the library, derived from [Song]s and [Album]s respectively.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Artist : MusicParent {
|
||||
|
@ -336,6 +346,7 @@ interface Artist : MusicParent {
|
|||
|
||||
/**
|
||||
* A genre.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Genre : MusicParent {
|
||||
|
@ -350,6 +361,7 @@ interface Genre : MusicParent {
|
|||
/**
|
||||
* Run [Music.resolveName] on each instance in the given list and concatenate them into a [String]
|
||||
* in a localized manner.
|
||||
*
|
||||
* @param context [Context] required
|
||||
* @return A concatenated string.
|
||||
*/
|
||||
|
@ -359,6 +371,7 @@ fun <T : Music> List<T>.resolveNames(context: Context) =
|
|||
/**
|
||||
* Returns if [Music.rawName] matches for each item in a list. Useful for scenarios where the
|
||||
* display information of an item must be compared without a context.
|
||||
*
|
||||
* @param other The list of items to compare to.
|
||||
* @return True if they are the same (by [Music.rawName]), false otherwise.
|
||||
*/
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.oxycblt.auxio.IntegerTable
|
|||
|
||||
/**
|
||||
* Represents a data configuration corresponding to a specific type of [Music],
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
enum class MusicMode {
|
||||
|
@ -35,6 +36,7 @@ enum class MusicMode {
|
|||
|
||||
/**
|
||||
* The integer representation of this instance.
|
||||
*
|
||||
* @see fromIntCode
|
||||
*/
|
||||
val intCode: Int
|
||||
|
@ -49,6 +51,7 @@ enum class MusicMode {
|
|||
companion object {
|
||||
/**
|
||||
* Convert a [MusicMode] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [MusicMode]
|
||||
* @return The corresponding [MusicMode], or null if the [MusicMode] is invalid.
|
||||
* @see MusicMode.intCode
|
||||
|
|
|
@ -40,6 +40,7 @@ interface MusicRepository {
|
|||
/**
|
||||
* Add a [Listener] to this instance. This can be used to receive changes in the music library.
|
||||
* Will invoke all [Listener] methods to initialize the instance with the current state.
|
||||
*
|
||||
* @param listener The [Listener] to add.
|
||||
* @see Listener
|
||||
*/
|
||||
|
@ -47,6 +48,7 @@ interface MusicRepository {
|
|||
|
||||
/**
|
||||
* Remove a [Listener] from this instance, preventing it from receiving any further updates.
|
||||
*
|
||||
* @param listener The [Listener] to remove. Does nothing if the [Listener] was never added in
|
||||
* the first place.
|
||||
* @see Listener
|
||||
|
@ -57,6 +59,7 @@ interface MusicRepository {
|
|||
interface Listener {
|
||||
/**
|
||||
* Called when the current [Library] has changed.
|
||||
*
|
||||
* @param library The new [Library], or null if no [Library] has been loaded yet.
|
||||
*/
|
||||
fun onLibraryChanged(library: Library?)
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.getSystemServiceCompat
|
|||
|
||||
/**
|
||||
* User configuration specific to music system.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface MusicSettings : Settings<MusicSettings.Listener> {
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.system.Indexer
|
|||
|
||||
/**
|
||||
* A [ViewModel] providing data specific to the music loading process.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -76,6 +77,7 @@ class MusicViewModel @Inject constructor(private val indexer: Indexer) :
|
|||
|
||||
/**
|
||||
* Non-manipulated statistics bound the last successful music load.
|
||||
*
|
||||
* @param songs The amount of [Song]s that were loaded.
|
||||
* @param albums The amount of [Album]s that were created.
|
||||
* @param artists The amount of [Artist]s that were created.
|
||||
|
|
|
@ -23,17 +23,20 @@ import org.oxycblt.auxio.util.*
|
|||
|
||||
/**
|
||||
* A repository allowing access to cached metadata obtained in prior music loading operations.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface CacheRepository {
|
||||
/**
|
||||
* Read the current [Cache], if it exists.
|
||||
*
|
||||
* @return The stored [Cache], or null if it could not be obtained.
|
||||
*/
|
||||
suspend fun readCache(): Cache?
|
||||
|
||||
/**
|
||||
* Write the list of newly-loaded [RawSong]s to the cache, replacing the prior data.
|
||||
*
|
||||
* @param rawSongs The [rawSongs] to write to the cache.
|
||||
*/
|
||||
suspend fun writeCache(rawSongs: List<RawSong>)
|
||||
|
@ -67,6 +70,7 @@ class CacheRepositoryImpl @Inject constructor(private val cachedSongsDao: Cached
|
|||
/**
|
||||
* A cache of music metadata obtained in prior music loading operations. Obtain an instance with
|
||||
* [CacheRepository].
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface Cache {
|
||||
|
@ -75,6 +79,7 @@ interface Cache {
|
|||
|
||||
/**
|
||||
* Populate a [RawSong] from a cache entry, if it exists.
|
||||
*
|
||||
* @param rawSong The [RawSong] to populate.
|
||||
* @return true if a cache entry could be applied to [rawSong], false otherwise.
|
||||
*/
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.oxycblt.auxio.util.logW
|
|||
|
||||
/**
|
||||
* The properties of a [Song]'s file.
|
||||
*
|
||||
* @param bitrateKbps The bit rate, in kilobytes-per-second. Null if it could not be parsed.
|
||||
* @param sampleRateHz The sample rate, in hertz.
|
||||
* @param resolvedMimeType The known mime type of the [Song] after it's file format was determined.
|
||||
|
@ -44,6 +45,7 @@ data class AudioInfo(
|
|||
interface Provider {
|
||||
/**
|
||||
* Extract the [AudioInfo] of a given [Song].
|
||||
*
|
||||
* @param song The [Song] to read.
|
||||
* @return The [AudioInfo] of the [Song], if possible to obtain.
|
||||
*/
|
||||
|
@ -53,6 +55,7 @@ data class AudioInfo(
|
|||
|
||||
/**
|
||||
* A framework-backed implementation of [AudioInfo.Provider].
|
||||
*
|
||||
* @param context [Context] required to read audio files.
|
||||
*/
|
||||
class AudioInfoProviderImpl @Inject constructor(@ApplicationContext private val context: Context) :
|
||||
|
|
|
@ -44,6 +44,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Resolve this instance into a human-readable date.
|
||||
*
|
||||
* @param context [Context] required to get human-readable names.
|
||||
* @return If the [Date] has a valid month and year value, a more fine-grained date (ex. "Jan
|
||||
* 2020") will be returned. Otherwise, a plain year value (ex. "2020") is returned. Dates will
|
||||
|
@ -115,6 +116,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
* A range of [Date]s. This is used in contexts where the [Date] of an item is derived from
|
||||
* several sub-items and thus can have a "range" of release dates. Use [from] to create an
|
||||
* instance.
|
||||
*
|
||||
* @author Alexander Capehart
|
||||
*/
|
||||
class Range
|
||||
|
@ -127,6 +129,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Resolve this instance into a human-readable date range.
|
||||
*
|
||||
* @param context [Context] required to get human-readable names.
|
||||
* @return If the date has a maximum value, then a `min - max` formatted string will be
|
||||
* returned with the formatted [Date]s of the minimum and maximum dates respectively.
|
||||
|
@ -149,6 +152,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
companion object {
|
||||
/**
|
||||
* Create a [Range] from the given list of [Date]s.
|
||||
*
|
||||
* @param dates The [Date]s to use.
|
||||
* @return A [Range] based on the minimum and maximum [Date]s. If there are no [Date]s,
|
||||
* null is returned.
|
||||
|
@ -186,6 +190,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Create a [Date] from a year component.
|
||||
*
|
||||
* @param year The year component.
|
||||
* @return A new [Date] of the given component, or null if the component is invalid.
|
||||
*/
|
||||
|
@ -204,6 +209,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Create a [Date] from a date component.
|
||||
*
|
||||
* @param year The year component.
|
||||
* @param month The month component.
|
||||
* @param day The day component.
|
||||
|
@ -214,6 +220,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Create [Date] from a datetime component.
|
||||
*
|
||||
* @param year The year component.
|
||||
* @param month The month component.
|
||||
* @param day The day component.
|
||||
|
@ -226,10 +233,11 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Create a [Date] from a [String] timestamp.
|
||||
*
|
||||
* @param timestamp The ISO-8601 timestamp to parse. Can have reduced precision.
|
||||
* @return A new [Date] consisting of the given components. May have reduced precision if
|
||||
* the components were partially invalid, and will be null if all components are invalid or
|
||||
* if the timestamp is invalid.
|
||||
* the components were partially invalid, and will be null if all components are invalid
|
||||
* or if the timestamp is invalid.
|
||||
*/
|
||||
fun from(timestamp: String): Date? {
|
||||
val tokens =
|
||||
|
@ -245,6 +253,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
|
||||
/**
|
||||
* Create a [Date] from the given non-validated tokens.
|
||||
*
|
||||
* @param tokens The tokens to use for each date component, in order of precision.
|
||||
* @return A new [Date] consisting of the given components. May have reduced precision if
|
||||
* the components were partially invalid, and will be null if all components are invalid.
|
||||
|
@ -262,6 +271,7 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
/**
|
||||
* Validate a list of tokens provided by [src], and add the valid ones to [dst]. Will stop
|
||||
* as soon as an invalid token is found.
|
||||
*
|
||||
* @param src The input tokens to validate.
|
||||
* @param dst The destination list to add valid tokens to.
|
||||
*/
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.oxycblt.auxio.list.Item
|
|||
|
||||
/**
|
||||
* A disc identifier for a song.
|
||||
*
|
||||
* @param number The disc number.
|
||||
* @param name The name of the disc group, if any. Null if not present.
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.R
|
|||
*
|
||||
* This class is derived from the MusicBrainz Release Group Type specification. It can be found at:
|
||||
* https://musicbrainz.org/doc/Release_Group/Type
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed class ReleaseType {
|
||||
|
@ -38,6 +39,7 @@ sealed class ReleaseType {
|
|||
|
||||
/**
|
||||
* A plain album.
|
||||
*
|
||||
* @param refinement A specification of what kind of performance this release is. If null, the
|
||||
* release is considered "Plain".
|
||||
*/
|
||||
|
@ -54,6 +56,7 @@ sealed class ReleaseType {
|
|||
|
||||
/**
|
||||
* A "Extended Play", or EP. Usually a smaller release consisting of 4-5 songs.
|
||||
*
|
||||
* @param refinement A specification of what kind of performance this release is. If null, the
|
||||
* release is considered "Plain".
|
||||
*/
|
||||
|
@ -70,6 +73,7 @@ sealed class ReleaseType {
|
|||
|
||||
/**
|
||||
* A single. Usually a release consisting of 1-2 songs.
|
||||
*
|
||||
* @param refinement A specification of what kind of performance this release is. If null, the
|
||||
* release is considered "Plain".
|
||||
*/
|
||||
|
@ -86,6 +90,7 @@ sealed class ReleaseType {
|
|||
|
||||
/**
|
||||
* A compilation. Usually consists of many songs from a variety of artists.
|
||||
*
|
||||
* @param refinement A specification of what kind of performance this release is. If null, the
|
||||
* release is considered "Plain".
|
||||
*/
|
||||
|
@ -149,6 +154,7 @@ sealed class ReleaseType {
|
|||
/**
|
||||
* Parse a [ReleaseType] from a string formatted with the MusicBrainz Release Group Type
|
||||
* specification.
|
||||
*
|
||||
* @param types A list of values consisting of valid release type values.
|
||||
* @return A [ReleaseType] consisting of the given types, or null if the types were not
|
||||
* valid.
|
||||
|
@ -170,6 +176,7 @@ sealed class ReleaseType {
|
|||
/**
|
||||
* Parse "secondary" types (i.e not [Album], [EP], or [Single]) from a string formatted with
|
||||
* the MusicBrainz Release Group Type specification.
|
||||
*
|
||||
* @param index The index of the release type to parse.
|
||||
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType]
|
||||
* corresponding to the callee's context. This is used in order to handle secondary times
|
||||
|
@ -194,6 +201,7 @@ sealed class ReleaseType {
|
|||
/**
|
||||
* Parse "secondary" types (i.e not [Album], [EP], [Single]) that do not correspond to any
|
||||
* child values.
|
||||
*
|
||||
* @param type The release type value to parse.
|
||||
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType]
|
||||
* corresponding to the callee's context. This is used in order to handle secondary times
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.oxycblt.auxio.music.metadata
|
|||
|
||||
/**
|
||||
* Defines the allowed separator characters that can be used to delimit multi-value tags.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
object Separators {
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
|||
/**
|
||||
* A [ViewBindingDialogFragment] that allows the user to configure the separator characters used to
|
||||
* split tags with multiple values.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -43,6 +43,7 @@ interface TagExtractor {
|
|||
/**
|
||||
* Extract the metadata of songs from [incompleteSongs] and send them to [completeSongs]. Will
|
||||
* terminate as soon as [incompleteSongs] is closed.
|
||||
*
|
||||
* @param incompleteSongs A [Channel] of incomplete songs to process.
|
||||
* @param completeSongs A [Channel] to send completed songs to.
|
||||
*/
|
||||
|
@ -105,6 +106,7 @@ class TagExtractorImpl @Inject constructor(@ApplicationContext private val conte
|
|||
|
||||
/**
|
||||
* Wraps a [TagExtractor] future and processes it into a [RawSong] when completed.
|
||||
*
|
||||
* @param context [Context] required to open the audio file.
|
||||
* @param rawSong [RawSong] to process.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
@ -121,6 +123,7 @@ private class Task(context: Context, private val rawSong: RawSong) {
|
|||
|
||||
/**
|
||||
* Try to get a completed song from this [Task], if it has finished processing.
|
||||
*
|
||||
* @return A [RawSong] instance if processing has completed, null otherwise.
|
||||
*/
|
||||
fun get(): RawSong? {
|
||||
|
@ -156,6 +159,7 @@ private class Task(context: Context, private val rawSong: RawSong) {
|
|||
|
||||
/**
|
||||
* Complete this instance's [RawSong] with ID3v2 Text Identification Frames.
|
||||
*
|
||||
* @param textFrames A mapping between ID3v2 Text Identification Frame IDs and one or more
|
||||
* values.
|
||||
*/
|
||||
|
@ -220,6 +224,7 @@ private class Task(context: Context, private val rawSong: RawSong) {
|
|||
/**
|
||||
* Parses the ID3v2.3 timestamp specification into a [Date] from the given Text Identification
|
||||
* Frames.
|
||||
*
|
||||
* @param textFrames A mapping between ID3v2 Text Identification Frame IDs and one or more
|
||||
* values.
|
||||
* @return A [Date] of a year value from TORY/TYER, a month and day value from TDAT, and a
|
||||
|
@ -261,6 +266,7 @@ private class Task(context: Context, private val rawSong: RawSong) {
|
|||
|
||||
/**
|
||||
* Complete this instance's [RawSong] with Vorbis comments.
|
||||
*
|
||||
* @param comments A mapping between vorbis comment names and one or more vorbis comment values.
|
||||
*/
|
||||
private fun populateWithVorbis(comments: Map<String, List<String>>) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.oxycblt.auxio.util.nonZeroOrNull
|
|||
* Parse a multi-value tag based on the user configuration. If the value is already composed of more
|
||||
* than one value, nothing is done. Otherwise, this function will attempt to split it based on the
|
||||
* user's separator preferences.
|
||||
*
|
||||
* @param settings [MusicSettings] required to obtain user separator configuration.
|
||||
* @return A new list of one or more [String]s.
|
||||
*/
|
||||
|
@ -40,6 +41,7 @@ fun List<String>.parseMultiValue(settings: MusicSettings) =
|
|||
/**
|
||||
* Split a [String] by the given selector, automatically handling escaped characters that satisfy
|
||||
* the selector.
|
||||
*
|
||||
* @param selector A block that determines if the string should be split at a given character.
|
||||
* @return One or more [String]s split by the selector.
|
||||
*/
|
||||
|
@ -83,6 +85,7 @@ inline fun String.splitEscaped(selector: (Char) -> Boolean): List<String> {
|
|||
|
||||
/**
|
||||
* Fix trailing whitespace or blank contents in a [String].
|
||||
*
|
||||
* @return A string with trailing whitespace remove,d or null if the [String] was all whitespace or
|
||||
* empty.
|
||||
*/
|
||||
|
@ -90,12 +93,14 @@ fun String.correctWhitespace() = trim().ifBlank { null }
|
|||
|
||||
/**
|
||||
* Fix trailing whitespace or blank contents within a list of [String]s.
|
||||
*
|
||||
* @return A list of non-blank strings with trailing whitespace removed.
|
||||
*/
|
||||
fun List<String>.correctWhitespace() = mapNotNull { it.correctWhitespace() }
|
||||
|
||||
/**
|
||||
* Attempt to parse a string by the user's separator preferences.
|
||||
*
|
||||
* @param settings [MusicSettings] required to obtain user separator configuration.
|
||||
* @return A list of one or more [String]s that were split up by the user-defined separators.
|
||||
*/
|
||||
|
@ -109,9 +114,11 @@ private fun String.maybeParseBySeparators(settings: MusicSettings): List<String>
|
|||
/**
|
||||
* Parse an ID3v2-style position + total [String] field. These fields consist of a number and an
|
||||
* (optional) total value delimited by a /.
|
||||
*
|
||||
* @return The position value extracted from the string field, or null if:
|
||||
* - The position could not be parsed
|
||||
* - The position was zeroed AND the total value was not present/zeroed
|
||||
*
|
||||
* @see transformPositionField
|
||||
*/
|
||||
fun String.parseId3v2PositionField() =
|
||||
|
@ -122,11 +129,13 @@ fun String.parseId3v2PositionField() =
|
|||
/**
|
||||
* Parse a vorbis-style position + total field. These fields consist of two fields for the position
|
||||
* and total numbers.
|
||||
*
|
||||
* @param pos The position value, or null if not present.
|
||||
* @param total The total value, if not present.
|
||||
* @return The position value extracted from the field, or null if:
|
||||
* - The position could not be parsed
|
||||
* - The position was zeroed AND the total value was not present/zeroed
|
||||
*
|
||||
* @see transformPositionField
|
||||
*/
|
||||
fun parseVorbisPositionField(pos: String?, total: String?) =
|
||||
|
@ -134,6 +143,7 @@ fun parseVorbisPositionField(pos: String?, total: String?) =
|
|||
|
||||
/**
|
||||
* Transform a raw position + total field into a position a way that tolerates placeholder values.
|
||||
*
|
||||
* @param pos The position value, or null if not present.
|
||||
* @param total The total value, if not present.
|
||||
* @return The position value extracted from the field, or null if:
|
||||
|
@ -151,6 +161,7 @@ fun transformPositionField(pos: Int?, total: Int?) =
|
|||
* Parse a multi-value genre name using ID3 rules. This will convert any ID3v1 integer
|
||||
* representations of genre fields into their named counterparts, and split up singular ID3v2-style
|
||||
* integer genre fields into one or more genres.
|
||||
*
|
||||
* @param settings [MusicSettings] required to obtain user separator configuration.
|
||||
* @return A list of one or more genre names..
|
||||
*/
|
||||
|
@ -164,6 +175,7 @@ fun List<String>.parseId3GenreNames(settings: MusicSettings) =
|
|||
|
||||
/**
|
||||
* Parse a single ID3v1/ID3v2 integer genre field into their named representations.
|
||||
*
|
||||
* @param settings [MusicSettings] required to obtain user separator configuration.
|
||||
* @return A list of one or more genre names.
|
||||
*/
|
||||
|
@ -172,6 +184,7 @@ private fun String.parseId3MultiValueGenre(settings: MusicSettings) =
|
|||
|
||||
/**
|
||||
* Parse an ID3v1 integer genre field.
|
||||
*
|
||||
* @return A named genre if the field is a valid integer, "Cover" or "Remix" if the field is
|
||||
* "CR"/"RX" respectively, and nothing if the field is not a valid ID3v1 integer genre.
|
||||
*/
|
||||
|
@ -200,6 +213,7 @@ private val ID3V2_GENRE_RE = Regex("((?:\\((\\d+|RX|CR)\\))*)(.+)?")
|
|||
/**
|
||||
* Parse an ID3v2 integer genre field, which has support for multiple genre values and combined
|
||||
* named/integer genres.
|
||||
*
|
||||
* @return A list of one or more genres, or null if the field is not a valid ID3v2 integer genre.
|
||||
*/
|
||||
private fun String.parseId3v2Genre(): List<String>? {
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
|||
|
||||
/**
|
||||
* Processing wrapper for [Metadata] that allows organized access to text-based audio tags.
|
||||
*
|
||||
* @param metadata The [Metadata] to wrap.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -79,6 +80,7 @@ class TextTags(metadata: Metadata) {
|
|||
|
||||
/**
|
||||
* Copies and sanitizes a possibly invalid string outputted from ExoPlayer.
|
||||
*
|
||||
* @return A new string allocated in a memory-safe manner with any UTF-8 errors replaced with
|
||||
* the Unicode replacement byte sequence.
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,7 @@ interface Library {
|
|||
|
||||
/**
|
||||
* Finds a [Music] item [T] in the library by it's [Music.UID].
|
||||
*
|
||||
* @param uid The [Music.UID] to search for.
|
||||
* @return The [T] corresponding to the given [Music.UID], or null if nothing could be found or
|
||||
* the [Music.UID] did not correspond to a [T].
|
||||
|
@ -55,6 +56,7 @@ interface Library {
|
|||
|
||||
/**
|
||||
* Convert a [Song] from an another library into a [Song] in this [Library].
|
||||
*
|
||||
* @param song The [Song] to convert.
|
||||
* @return The analogous [Song] in this [Library], or null if it does not exist.
|
||||
*/
|
||||
|
@ -62,6 +64,7 @@ interface Library {
|
|||
|
||||
/**
|
||||
* Convert a [MusicParent] from an another library into a [MusicParent] in this [Library].
|
||||
*
|
||||
* @param parent The [MusicParent] to convert.
|
||||
* @return The analogous [Album] in this [Library], or null if it does not exist.
|
||||
*/
|
||||
|
@ -69,6 +72,7 @@ interface Library {
|
|||
|
||||
/**
|
||||
* Find a [Song] instance corresponding to the given Intent.ACTION_VIEW [Uri].
|
||||
*
|
||||
* @param context [Context] required to analyze the [Uri].
|
||||
* @param uri [Uri] to search for.
|
||||
* @return A [Song] corresponding to the given [Uri], or null if one could not be found.
|
||||
|
@ -78,6 +82,7 @@ interface Library {
|
|||
companion object {
|
||||
/**
|
||||
* Create an instance of [Library].
|
||||
*
|
||||
* @param rawSongs [RawSong]s to create the library out of.
|
||||
* @param settings [MusicSettings] required.
|
||||
*/
|
||||
|
@ -117,6 +122,7 @@ private class LibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings) : Li
|
|||
|
||||
/**
|
||||
* Finds a [Music] item [T] in the library by it's [Music.UID].
|
||||
*
|
||||
* @param uid The [Music.UID] to search for.
|
||||
* @return The [T] corresponding to the given [Music.UID], or null if nothing could be found or
|
||||
* the [Music.UID] did not correspond to a [T].
|
||||
|
@ -141,6 +147,7 @@ private class LibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings) : Li
|
|||
|
||||
/**
|
||||
* Build a list [SongImpl]s from the given [RawSong].
|
||||
*
|
||||
* @param rawSongs The [RawSong]s to build the [SongImpl]s from.
|
||||
* @param settings [MusicSettings] to obtain user parsing configuration.
|
||||
* @return A sorted list of [SongImpl]s derived from the [RawSong] that should be suitable for
|
||||
|
@ -152,6 +159,7 @@ private class LibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings) : Li
|
|||
|
||||
/**
|
||||
* Build a list of [Album]s from the given [Song]s.
|
||||
*
|
||||
* @param songs The [Song]s to build [Album]s from. These will be linked with their respective
|
||||
* [Album]s when created.
|
||||
* @param settings [MusicSettings] to obtain user parsing configuration.
|
||||
|
@ -171,6 +179,7 @@ private class LibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings) : Li
|
|||
* Group up [Song]s and [Album]s into [Artist] instances. Both of these items are required as
|
||||
* they group into [Artist] instances much differently, with [Song]s being grouped primarily by
|
||||
* artist names, and [Album]s being grouped primarily by album artist names.
|
||||
*
|
||||
* @param songs The [Song]s to build [Artist]s from. One [Song] can result in the creation of
|
||||
* one or more [Artist] instances. These will be linked with their respective [Artist]s when
|
||||
* created.
|
||||
|
@ -210,6 +219,7 @@ private class LibraryImpl(rawSongs: List<RawSong>, settings: MusicSettings) : Li
|
|||
|
||||
/**
|
||||
* Group up [Song]s into [Genre] instances.
|
||||
*
|
||||
* @param [songs] The [Song]s to build [Genre]s from. One [Song] can result in the creation of
|
||||
* one or more [Genre] instances. These will be linked with their respective [Genre]s when
|
||||
* created.
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* Library-backed implementation of [Song].
|
||||
*
|
||||
* @param rawSong The [RawSong] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
@ -164,6 +165,7 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
|
|||
|
||||
/**
|
||||
* Links this [Song] with a parent [Album].
|
||||
*
|
||||
* @param album The parent [Album] to link to.
|
||||
*/
|
||||
fun link(album: AlbumImpl) {
|
||||
|
@ -172,6 +174,7 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
|
|||
|
||||
/**
|
||||
* Links this [Song] with a parent [Artist].
|
||||
*
|
||||
* @param artist The parent [Artist] to link to.
|
||||
*/
|
||||
fun link(artist: ArtistImpl) {
|
||||
|
@ -180,6 +183,7 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
|
|||
|
||||
/**
|
||||
* Links this [Song] with a parent [Genre].
|
||||
*
|
||||
* @param genre The parent [Genre] to link to.
|
||||
*/
|
||||
fun link(genre: GenreImpl) {
|
||||
|
@ -188,6 +192,7 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
|
|||
|
||||
/**
|
||||
* Perform final validation and organization on this instance.
|
||||
*
|
||||
* @return This instance upcasted to [Song].
|
||||
*/
|
||||
fun finalize(): Song {
|
||||
|
@ -218,6 +223,7 @@ class SongImpl(rawSong: RawSong, musicSettings: MusicSettings) : Song {
|
|||
|
||||
/**
|
||||
* Library-backed implementation of [Album].
|
||||
*
|
||||
* @param rawAlbum The [RawAlbum] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param songs The [Song]s that are a part of this [Album]. These items will be linked to this
|
||||
|
@ -286,6 +292,7 @@ class AlbumImpl(
|
|||
|
||||
/**
|
||||
* Links this [Album] with a parent [Artist].
|
||||
*
|
||||
* @param artist The parent [Artist] to link to.
|
||||
*/
|
||||
fun link(artist: ArtistImpl) {
|
||||
|
@ -294,6 +301,7 @@ class AlbumImpl(
|
|||
|
||||
/**
|
||||
* Perform final validation and organization on this instance.
|
||||
*
|
||||
* @return This instance upcasted to [Album].
|
||||
*/
|
||||
fun finalize(): Album {
|
||||
|
@ -313,11 +321,12 @@ class AlbumImpl(
|
|||
|
||||
/**
|
||||
* Library-backed implementation of [Artist].
|
||||
*
|
||||
* @param rawArtist The [RawArtist] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param songAlbums A list of the [Song]s and [Album]s that are a part of this [Artist] , either
|
||||
* through artist or album artist tags. Providing [Song]s to the artist is optional. These instances
|
||||
* will be linked to this [Artist].
|
||||
* through artist or album artist tags. Providing [Song]s to the artist is optional. These
|
||||
* instances will be linked to this [Artist].
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ArtistImpl(
|
||||
|
@ -379,6 +388,7 @@ class ArtistImpl(
|
|||
* Returns the original position of this [Artist]'s [RawArtist] within the given [RawArtist]
|
||||
* list. This can be used to create a consistent ordering within child [Artist] lists based on
|
||||
* the original tag order.
|
||||
*
|
||||
* @param rawArtists The [RawArtist] instances to check. It is assumed that this [Artist]'s
|
||||
* [RawArtist] will be within the list.
|
||||
* @return The index of the [Artist]'s [RawArtist] within the list.
|
||||
|
@ -387,6 +397,7 @@ class ArtistImpl(
|
|||
|
||||
/**
|
||||
* Perform final validation and organization on this instance.
|
||||
*
|
||||
* @return This instance upcasted to [Artist].
|
||||
*/
|
||||
fun finalize(): Artist {
|
||||
|
@ -400,6 +411,7 @@ class ArtistImpl(
|
|||
}
|
||||
/**
|
||||
* Library-backed implementation of [Genre].
|
||||
*
|
||||
* @param rawGenre [RawGenre] to derive the member data from.
|
||||
* @param musicSettings [MusicSettings] to for user parsing configuration.
|
||||
* @param songs Child [SongImpl]s of this instance.
|
||||
|
@ -450,6 +462,7 @@ class GenreImpl(
|
|||
* Returns the original position of this [Genre]'s [RawGenre] within the given [RawGenre] list.
|
||||
* This can be used to create a consistent ordering within child [Genre] lists based on the
|
||||
* original tag order.
|
||||
*
|
||||
* @param rawGenres The [RawGenre] instances to check. It is assumed that this [Genre] 's
|
||||
* [RawGenre] will be within the list.
|
||||
* @return The index of the [Genre]'s [RawGenre] within the list.
|
||||
|
@ -458,6 +471,7 @@ class GenreImpl(
|
|||
|
||||
/**
|
||||
* Perform final validation and organization on this instance.
|
||||
*
|
||||
* @return This instance upcasted to [Genre].
|
||||
*/
|
||||
fun finalize(): Music {
|
||||
|
@ -468,6 +482,7 @@ class GenreImpl(
|
|||
|
||||
/**
|
||||
* Update a [MessageDigest] with a lowercase [String].
|
||||
*
|
||||
* @param string The [String] to hash. If null, it will not be hashed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
|
@ -481,6 +496,7 @@ fun MessageDigest.update(string: String?) {
|
|||
|
||||
/**
|
||||
* Update a [MessageDigest] with the string representation of a [Date].
|
||||
*
|
||||
* @param date The [Date] to hash. If null, nothing will be done.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
|
@ -494,6 +510,7 @@ fun MessageDigest.update(date: Date?) {
|
|||
|
||||
/**
|
||||
* Update a [MessageDigest] with the lowercase versions of all of the input [String]s.
|
||||
*
|
||||
* @param strings The [String]s to hash. If a [String] is null, it will not be hashed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
|
@ -503,6 +520,7 @@ fun MessageDigest.update(strings: List<String?>) {
|
|||
|
||||
/**
|
||||
* Update a [MessageDigest] with the little-endian bytes of a [Int].
|
||||
*
|
||||
* @param n The [Int] to write. If null, nothing will be done.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
|
@ -520,6 +538,7 @@ private val COLLATOR: Collator = Collator.getInstance().apply { strength = Colla
|
|||
/**
|
||||
* Provided implementation to create a [CollationKey] in the way described by [Music.collationKey].
|
||||
* This should be used in all overrides of all [CollationKey].
|
||||
*
|
||||
* @param musicSettings [MusicSettings] required for user parsing configuration.
|
||||
* @return A [CollationKey] that follows the specification described by [Music.collationKey].
|
||||
*/
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.music.storage.Directory
|
|||
|
||||
/**
|
||||
* Raw information about a [SongImpl] obtained from the filesystem/Extractor instances.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class RawSong(
|
||||
|
@ -88,6 +89,7 @@ class RawSong(
|
|||
|
||||
/**
|
||||
* Raw information about an [AlbumImpl] obtained from the component [SongImpl] instances.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class RawAlbum(
|
||||
|
@ -134,6 +136,7 @@ class RawAlbum(
|
|||
/**
|
||||
* Raw information about an [ArtistImpl] obtained from the component [SongImpl] and [AlbumImpl]
|
||||
* instances.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class RawArtist(
|
||||
|
@ -175,6 +178,7 @@ class RawArtist(
|
|||
|
||||
/**
|
||||
* Raw information about a [GenreImpl] obtained from the component [SongImpl] instances.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class RawGenre(
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* [RecyclerView.Adapter] that manages a list of [Directory] instances.
|
||||
*
|
||||
* @param listener A [DirectoryAdapter.Listener] to bind interactions to.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -48,6 +49,7 @@ class DirectoryAdapter(private val listener: Listener) :
|
|||
|
||||
/**
|
||||
* Add a [Directory] to the end of the list.
|
||||
*
|
||||
* @param dir The [Directory] to add.
|
||||
*/
|
||||
fun add(dir: Directory) {
|
||||
|
@ -61,6 +63,7 @@ class DirectoryAdapter(private val listener: Listener) :
|
|||
|
||||
/**
|
||||
* Add a list of [Directory] instances to the end of the list.
|
||||
*
|
||||
* @param dirs The [Directory instances to add.
|
||||
*/
|
||||
fun addAll(dirs: List<Directory>) {
|
||||
|
@ -71,6 +74,7 @@ class DirectoryAdapter(private val listener: Listener) :
|
|||
|
||||
/**
|
||||
* Remove a [Directory] from the list.
|
||||
*
|
||||
* @param dir The [Directory] to remove. Must exist in the list.
|
||||
*/
|
||||
fun remove(dir: Directory) {
|
||||
|
@ -87,12 +91,14 @@ class DirectoryAdapter(private val listener: Listener) :
|
|||
|
||||
/**
|
||||
* A [RecyclerView.Recycler] that displays a [Directory]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class MusicDirViewHolder private constructor(private val binding: ItemMusicDirBinding) :
|
||||
DialogRecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param dir The new [Directory] to bind.
|
||||
* @param listener A [DirectoryAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
|
@ -104,6 +110,7 @@ class MusicDirViewHolder private constructor(private val binding: ItemMusicDirBi
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.oxycblt.auxio.R
|
|||
/**
|
||||
* A full absolute path to a file. Only intended for display purposes. For accessing files, URIs are
|
||||
* preferred in all cases due to scoped storage limitations.
|
||||
*
|
||||
* @param name The name of the file.
|
||||
* @param parent The parent [Directory] of the file.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
@ -36,6 +37,7 @@ data class Path(val name: String, val parent: Directory)
|
|||
|
||||
/**
|
||||
* A volume-aware relative path to a directory.
|
||||
*
|
||||
* @param volume The [StorageVolume] that the [Directory] is contained in.
|
||||
* @param relativePath The relative path from within the [StorageVolume] to the [Directory].
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
|
@ -43,6 +45,7 @@ data class Path(val name: String, val parent: Directory)
|
|||
class Directory private constructor(val volume: StorageVolume, val relativePath: String) {
|
||||
/**
|
||||
* Resolve the [Directory] instance into a human-readable path name.
|
||||
*
|
||||
* @param context [Context] required to obtain volume descriptions.
|
||||
* @return A human-readable path.
|
||||
* @see StorageVolume.getDescription
|
||||
|
@ -55,6 +58,7 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
* violation of the document tree URI contract, but it's also the only one can sensibly work
|
||||
* with these uris in the UI, and it doesn't exactly matter since we never write or read to
|
||||
* directory.
|
||||
*
|
||||
* @return A URI [String] abiding by the document tree specification, or null if the [Directory]
|
||||
* is not valid.
|
||||
*/
|
||||
|
@ -84,6 +88,7 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
|
||||
/**
|
||||
* Create a new directory instance from the given components.
|
||||
*
|
||||
* @param volume The [StorageVolume] that the [Directory] is contained in.
|
||||
* @param relativePath The relative path from within the [StorageVolume] to the [Directory].
|
||||
* Will be stripped of any trailing separators for a consistent internal representation.
|
||||
|
@ -97,6 +102,7 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
* Create a new directory from a document tree URI. This is a huge violation of the document
|
||||
* tree URI contract, but it's also the only one can sensibly work with these uris in the
|
||||
* UI, and it doesn't exactly matter since we never write or read directory.
|
||||
*
|
||||
* @param storageManager [StorageManager] in order to obtain the [StorageVolume] specified
|
||||
* in the given URI.
|
||||
* @param uri The URI string to parse into a [Directory].
|
||||
|
@ -123,6 +129,7 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
|
||||
/**
|
||||
* Represents the configuration for specific directories to filter to/from when loading music.
|
||||
*
|
||||
* @param dirs A list of [Directory] instances. How these are interpreted depends on [shouldInclude]
|
||||
* @param shouldInclude True if the library should only load from the [Directory] instances, false
|
||||
* if the library should not load from the [Directory] instances.
|
||||
|
@ -132,6 +139,7 @@ data class MusicDirectories(val dirs: List<Directory>, val shouldInclude: Boolea
|
|||
|
||||
/**
|
||||
* A mime type of a file. Only intended for display.
|
||||
*
|
||||
* @param fromExtension The mime type obtained by analyzing the file extension.
|
||||
* @param fromFormat The mime type obtained by analyzing the file format. Null if could not be
|
||||
* obtained.
|
||||
|
@ -140,6 +148,7 @@ data class MusicDirectories(val dirs: List<Directory>, val shouldInclude: Boolea
|
|||
data class MimeType(val fromExtension: String, val fromFormat: String?) {
|
||||
/**
|
||||
* Resolve the mime type into a human-readable format name, such as "Ogg Vorbis".
|
||||
*
|
||||
* @param context [Context] required to obtain human-readable strings.
|
||||
* @return A human-readable name for this mime type. Will first try [fromFormat], then falling
|
||||
* back to [fromExtension], and then null if that fails.
|
||||
|
|
|
@ -42,17 +42,20 @@ import org.oxycblt.auxio.util.logD
|
|||
* music extraction process and primarily intended for redundancy for files not natively supported
|
||||
* by other extractors. Solely relying on this is not recommended, as it often produces bad
|
||||
* metadata.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface MediaStoreExtractor {
|
||||
/**
|
||||
* Query the media database.
|
||||
*
|
||||
* @return A new [Query] returned from the media database.
|
||||
*/
|
||||
suspend fun query(): Query
|
||||
|
||||
/**
|
||||
* Consume the [Cursor] loaded after [query].
|
||||
*
|
||||
* @param query The [Query] to consume.
|
||||
* @param cache A [Cache] used to avoid extracting metadata for cached songs, or null if no
|
||||
* [Cache] was available.
|
||||
|
@ -79,6 +82,7 @@ interface MediaStoreExtractor {
|
|||
companion object {
|
||||
/**
|
||||
* Create a framework-backed instance.
|
||||
*
|
||||
* @param context [Context] required.
|
||||
* @param musicSettings [MusicSettings] required.
|
||||
* @return A new [MediaStoreExtractor] that will work best on the device's API level.
|
||||
|
@ -172,7 +176,8 @@ private abstract class BaseMediaStoreExtractor(
|
|||
cursor.getColumnIndexOrThrow(MediaStore.Audio.Genres.Members._ID)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
// Assume that a song can't inhabit multiple genre entries, as I doubt
|
||||
// Assume that a song can't inhabit multiple genre entries, as I
|
||||
// doubt
|
||||
// MediaStore is actually aware that songs can have multiple genres.
|
||||
genreNamesMap[cursor.getLong(songIdIndex)] = name
|
||||
}
|
||||
|
@ -232,12 +237,14 @@ private abstract class BaseMediaStoreExtractor(
|
|||
/**
|
||||
* The companion template to add to the projection's selector whenever arguments are added by
|
||||
* [addDirToSelector].
|
||||
*
|
||||
* @see addDirToSelector
|
||||
*/
|
||||
protected abstract val dirSelectorTemplate: String
|
||||
|
||||
/**
|
||||
* Add a [Directory] to the given list of projection selector arguments.
|
||||
*
|
||||
* @param dir The [Directory] to add.
|
||||
* @param args The destination list to append selector arguments to that are analogous to the
|
||||
* given [Directory].
|
||||
|
@ -431,6 +438,7 @@ private class Api21MediaStoreExtractor(context: Context, musicSettings: MusicSet
|
|||
|
||||
/**
|
||||
* A [BaseMediaStoreExtractor] that implements common behavior supported from API 29 onwards.
|
||||
*
|
||||
* @param context [Context] required to query the media database.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -494,8 +502,8 @@ private abstract class BaseApi29MediaStoreExtractor(
|
|||
|
||||
/**
|
||||
* A [BaseMediaStoreExtractor] that completes the music loading process in a way compatible with at
|
||||
* API
|
||||
* 29.
|
||||
* API 29.
|
||||
*
|
||||
* @param context [Context] required to query the media database.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -535,6 +543,7 @@ private class Api29MediaStoreExtractor(context: Context, musicSettings: MusicSet
|
|||
/**
|
||||
* A [BaseMediaStoreExtractor] that completes the music loading process in a way compatible from API
|
||||
* 30 onwards.
|
||||
*
|
||||
* @param context [Context] required to query the media database.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -584,6 +593,7 @@ private class Api30MediaStoreExtractor(context: Context, musicSettings: MusicSet
|
|||
* Unpack the track number from a combined track + disc [Int] field. These fields appear within
|
||||
* MediaStore's TRACK column, and combine the track and disc value into a single field where the
|
||||
* disc number is the 4th+ digit.
|
||||
*
|
||||
* @return The track number extracted from the combined integer value, or null if the value was
|
||||
* zero.
|
||||
*/
|
||||
|
@ -593,6 +603,7 @@ private fun Int.unpackTrackNo() = transformPositionField(mod(1000), null)
|
|||
* Unpack the disc number from a combined track + disc [Int] field. These fields appear within
|
||||
* MediaStore's TRACK column, and combine the track and disc value into a single field where the
|
||||
* disc number is the 4th+ digit.
|
||||
*
|
||||
* @return The disc number extracted from the combined integer field, or null if the value was zero.
|
||||
*/
|
||||
private fun Int.unpackDiscNo() = transformPositionField(div(1000), null)
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
|
||||
/**
|
||||
* Dialog that manages the music dirs setting.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
@ -149,6 +150,7 @@ class MusicDirsDialog :
|
|||
|
||||
/**
|
||||
* Add a Document Tree [Uri] chosen by the user to the current [MusicDirectories] instance.
|
||||
*
|
||||
* @param uri The document tree [Uri] to add, chosen by the user. Will do nothing if the [Uri]
|
||||
* is null or not valid.
|
||||
*/
|
||||
|
|
|
@ -42,6 +42,7 @@ val Context.contentResolverSafe: ContentResolver
|
|||
|
||||
/**
|
||||
* A shortcut for querying the [ContentResolver] database.
|
||||
*
|
||||
* @param uri The [Uri] of content to retrieve.
|
||||
* @param projection A list of SQL columns to query from the database.
|
||||
* @param selector A SQL selection statement to filter results. Spaces where arguments should be
|
||||
|
@ -61,6 +62,7 @@ fun ContentResolver.safeQuery(
|
|||
/**
|
||||
* A shortcut for [safeQuery] with [use] applied, automatically cleaning up the [Cursor]'s resources
|
||||
* when no longer used.
|
||||
*
|
||||
* @param uri The [Uri] of content to retrieve.
|
||||
* @param projection A list of SQL columns to query from the database.
|
||||
* @param selector A SQL selection statement to filter results. Spaces where arguments should be
|
||||
|
@ -84,6 +86,7 @@ private val EXTERNAL_COVERS_URI = Uri.parse("content://media/external/audio/albu
|
|||
|
||||
/**
|
||||
* Convert a [MediaStore] Song ID into a [Uri] to it's audio file.
|
||||
*
|
||||
* @return An external storage audio file [Uri]. May not exist.
|
||||
* @see ContentUris.withAppendedId
|
||||
* @see MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
|
@ -94,6 +97,7 @@ fun Long.toAudioUri() =
|
|||
/**
|
||||
* Convert a [MediaStore] Album ID into a [Uri] to it's system-provided album cover. This cover will
|
||||
* be fast to load, but will be lower quality.
|
||||
*
|
||||
* @return An external storage image [Uri]. May not exist.
|
||||
* @see ContentUris.withAppendedId
|
||||
*/
|
||||
|
@ -105,6 +109,7 @@ fun Long.toCoverUri() = ContentUris.withAppendedId(EXTERNAL_COVERS_URI, this)
|
|||
/**
|
||||
* Provides the analogous method to [StorageManager.getStorageVolumes] method that is usable from
|
||||
* API 21 to API 23, in which the [StorageManager] API was hidden and differed greatly.
|
||||
*
|
||||
* @see StorageManager.getStorageVolumes
|
||||
*/
|
||||
@Suppress("NewApi")
|
||||
|
@ -114,6 +119,7 @@ private val SM_API21_GET_VOLUME_LIST_METHOD: Method by
|
|||
/**
|
||||
* Provides the analogous method to [StorageVolume.getDirectory] method that is usable from API 21
|
||||
* to API 23, in which the [StorageVolume] API was hidden and differed greatly.
|
||||
*
|
||||
* @see StorageVolume.getDirectory
|
||||
*/
|
||||
@Suppress("NewApi")
|
||||
|
@ -122,6 +128,7 @@ private val SV_API21_GET_PATH_METHOD: Method by lazyReflectedMethod(StorageVolum
|
|||
/**
|
||||
* The [StorageVolume] considered the "primary" volume by the system, obtained in a
|
||||
* version-compatible manner.
|
||||
*
|
||||
* @see StorageManager.getPrimaryStorageVolume
|
||||
* @see StorageVolume.isPrimary
|
||||
*/
|
||||
|
@ -131,6 +138,7 @@ val StorageManager.primaryStorageVolumeCompat: StorageVolume
|
|||
/**
|
||||
* The list of [StorageVolume]s currently recognized by [StorageManager], in a version-compatible
|
||||
* manner.
|
||||
*
|
||||
* @see StorageManager.getStorageVolumes
|
||||
*/
|
||||
val StorageManager.storageVolumesCompat: List<StorageVolume>
|
||||
|
@ -145,6 +153,7 @@ val StorageManager.storageVolumesCompat: List<StorageVolume>
|
|||
/**
|
||||
* The the absolute path to this [StorageVolume]'s directory within the file-system, in a
|
||||
* version-compatible manner. Will be null if the [StorageVolume] cannot be read.
|
||||
*
|
||||
* @see StorageVolume.getDirectory
|
||||
*/
|
||||
val StorageVolume.directoryCompat: String?
|
||||
|
@ -164,6 +173,7 @@ val StorageVolume.directoryCompat: String?
|
|||
|
||||
/**
|
||||
* Get the human-readable description of this volume, such as "Internal Shared Storage".
|
||||
*
|
||||
* @param context [Context] required to obtain human-readable string resources.
|
||||
* @return A human-readable name for this volume.
|
||||
*/
|
||||
|
@ -173,6 +183,7 @@ fun StorageVolume.getDescriptionCompat(context: Context): String = getDescriptio
|
|||
/**
|
||||
* If this [StorageVolume] is considered the "Primary" volume where the Android System is kept. May
|
||||
* still be a removable volume.
|
||||
*
|
||||
* @see StorageVolume.isPrimary
|
||||
*/
|
||||
val StorageVolume.isPrimaryCompat: Boolean
|
||||
|
@ -181,6 +192,7 @@ val StorageVolume.isPrimaryCompat: Boolean
|
|||
/**
|
||||
* If this storage is "emulated", i.e intrinsic to the device, obtained in a version compatible
|
||||
* manner.
|
||||
*
|
||||
* @see StorageVolume.isEmulated
|
||||
*/
|
||||
val StorageVolume.isEmulatedCompat: Boolean
|
||||
|
@ -198,6 +210,7 @@ val StorageVolume.isInternalCompat: Boolean
|
|||
/**
|
||||
* The unique identifier for this [StorageVolume], obtained in a version compatible manner. Can be
|
||||
* null.
|
||||
*
|
||||
* @see StorageVolume.getUuid
|
||||
*/
|
||||
val StorageVolume.uuidCompat: String?
|
||||
|
@ -206,6 +219,7 @@ val StorageVolume.uuidCompat: String?
|
|||
/**
|
||||
* The current state of this [StorageVolume], such as "mounted" or "read-only", obtained in a
|
||||
* version compatible manner.
|
||||
*
|
||||
* @see StorageVolume.getState
|
||||
*/
|
||||
val StorageVolume.stateCompat: String
|
||||
|
@ -214,6 +228,7 @@ val StorageVolume.stateCompat: String
|
|||
/**
|
||||
* Returns the name of this volume that can be used to interact with [MediaStore], in a version
|
||||
* compatible manner. Will be null if the volume is not scanned by [MediaStore].
|
||||
*
|
||||
* @see StorageVolume.getMediaStoreVolumeName
|
||||
*/
|
||||
val StorageVolume.mediaStoreVolumeNameCompat: String?
|
||||
|
|
|
@ -68,6 +68,7 @@ interface Indexer {
|
|||
* Register a [Controller] for this instance. This instance will handle any commands to start
|
||||
* the music loading process. There can be only one [Controller] at a time. Will invoke all
|
||||
* [Listener] methods to initialize the instance with the current state.
|
||||
*
|
||||
* @param controller The [Controller] to register. Will do nothing if already registered.
|
||||
*/
|
||||
fun registerController(controller: Controller)
|
||||
|
@ -75,6 +76,7 @@ interface Indexer {
|
|||
/**
|
||||
* Unregister the [Controller] from this instance, prevent it from recieving any further
|
||||
* commands.
|
||||
*
|
||||
* @param controller The [Controller] to unregister. Must be the current [Controller]. Does
|
||||
* nothing if invoked by another [Controller] implementation.
|
||||
*/
|
||||
|
@ -84,12 +86,14 @@ interface Indexer {
|
|||
* Register the [Listener] for this instance. This can be used to receive rapid-fire updates to
|
||||
* the current music loading state. There can be only one [Listener] at a time. Will invoke all
|
||||
* [Listener] methods to initialize the instance with the current state.
|
||||
*
|
||||
* @param listener The [Listener] to add.
|
||||
*/
|
||||
fun registerListener(listener: Listener)
|
||||
|
||||
/**
|
||||
* Unregister a [Listener] from this instance, preventing it from recieving any further updates.
|
||||
*
|
||||
* @param listener The [Listener] to unregister. Must be the current [Listener]. Does nothing if
|
||||
* invoked by another [Listener] implementation.
|
||||
* @see Listener
|
||||
|
@ -99,6 +103,7 @@ interface Indexer {
|
|||
/**
|
||||
* Start the indexing process. This should be done from in the background from [Controller]'s
|
||||
* context after a command has been received to start the process.
|
||||
*
|
||||
* @param context [Context] required to load music.
|
||||
* @param withCache Whether to use the cache or not when loading. If false, the cache will still
|
||||
* be written, but no cache entries will be loaded into the new library.
|
||||
|
@ -111,6 +116,7 @@ interface Indexer {
|
|||
* Request that the music library should be reloaded. This should be used by components that do
|
||||
* not manage the indexing process in order to signal that the [Indexer.Controller] should call
|
||||
* [index] eventually.
|
||||
*
|
||||
* @param withCache Whether to use the cache when loading music. Does nothing if there is no
|
||||
* [Indexer.Controller].
|
||||
*/
|
||||
|
@ -126,6 +132,7 @@ interface Indexer {
|
|||
sealed class State {
|
||||
/**
|
||||
* Music loading is ongoing.
|
||||
*
|
||||
* @param indexing The current music loading progress..
|
||||
* @see Indexer.Indexing
|
||||
*/
|
||||
|
@ -133,6 +140,7 @@ interface Indexer {
|
|||
|
||||
/**
|
||||
* Music loading has completed.
|
||||
*
|
||||
* @param result The outcome of the music loading process.
|
||||
*/
|
||||
data class Complete(val result: Result<Library>) : State()
|
||||
|
@ -140,6 +148,7 @@ interface Indexer {
|
|||
|
||||
/**
|
||||
* Represents the current progress of the music loader. Usually encapsulated in a [State].
|
||||
*
|
||||
* @see State.Indexing
|
||||
*/
|
||||
sealed class Indexing {
|
||||
|
@ -150,6 +159,7 @@ interface Indexer {
|
|||
|
||||
/**
|
||||
* Music loading has a definite progress.
|
||||
*
|
||||
* @param current The current amount of songs that have been loaded.
|
||||
* @param total The projected total amount of songs that will be loaded.
|
||||
*/
|
||||
|
@ -195,6 +205,7 @@ interface Indexer {
|
|||
/**
|
||||
* Called when a new music loading process was requested. Implementations should forward
|
||||
* this to [index].
|
||||
*
|
||||
* @param withCache Whether to use the cache or not when loading. If false, the cache should
|
||||
* still be written, but no cache entries will be loaded into the new library.
|
||||
* @see index
|
||||
|
@ -390,6 +401,7 @@ constructor(
|
|||
* Emit a new [Indexer.State.Indexing] state. This can be used to signal the current state of
|
||||
* the music loading process to external code. Assumes that the callee has already checked if
|
||||
* they have not been canceled and thus have the ability to emit a new state.
|
||||
*
|
||||
* @param indexing The new [Indexer.Indexing] state to emit, or null if no loading process is
|
||||
* occurring.
|
||||
*/
|
||||
|
@ -409,6 +421,7 @@ constructor(
|
|||
* Emit a new [Indexer.State.Complete] state. This can be used to signal the completion of the
|
||||
* music loading process to external code. Will check if the callee has not been canceled and
|
||||
* thus has the ability to emit a new state
|
||||
*
|
||||
* @param result The new [Result] to emit, representing the outcome of the music loading
|
||||
* process.
|
||||
*/
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.oxycblt.auxio.util.newMainPendingIntent
|
|||
|
||||
/**
|
||||
* A dynamic [ForegroundServiceNotification] that shows the current music loading state.
|
||||
*
|
||||
* @param context [Context] required to create the notification.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
@ -53,6 +54,7 @@ class IndexingNotification(private val context: Context) :
|
|||
|
||||
/**
|
||||
* Update this notification with the new music loading state.
|
||||
*
|
||||
* @param indexing The new music loading state to display in the notification.
|
||||
* @return true if the notification updated, false otherwise
|
||||
*/
|
||||
|
@ -90,6 +92,7 @@ class IndexingNotification(private val context: Context) :
|
|||
/**
|
||||
* A static [ForegroundServiceNotification] that signals to the user that the app is currently
|
||||
* monitoring the music library for changes.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ObservingNotification(context: Context) :
|
||||
|
|
|
@ -50,9 +50,9 @@ import org.oxycblt.auxio.util.logD
|
|||
* This [Service] also handles automatic rescanning, as that is a similarly long-running background
|
||||
* operation that would be unsuitable elsewhere in the app.
|
||||
*
|
||||
* TODO: Unify with PlaybackService as part of the service independence project
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Unify with PlaybackService as part of the service independence project
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
||||
|
@ -176,6 +176,7 @@ class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
|||
/**
|
||||
* Update the current state to "Active", in which the service signals that music loading is
|
||||
* on-going.
|
||||
*
|
||||
* @param state The current music loading state.
|
||||
*/
|
||||
private fun updateActiveSession(state: Indexer.Indexing) {
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* An [RecyclerView.Adapter] that displays a list of [Artist] choices.
|
||||
*
|
||||
* @param listener A [ClickableListListener] to bind interactions to.
|
||||
* @author OxygenCobalt.
|
||||
*/
|
||||
|
@ -46,6 +47,7 @@ class ArtistChoiceAdapter(private val listener: ClickableListListener<Artist>) :
|
|||
|
||||
/**
|
||||
* Immediately update the [Artist] choices.
|
||||
*
|
||||
* @param newArtists The new [Artist]s to show.
|
||||
*/
|
||||
fun submitList(newArtists: List<Artist>) {
|
||||
|
@ -64,6 +66,7 @@ class ArtistChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|||
DialogRecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param artist The new [Artist] to bind.
|
||||
* @param listener A [ClickableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -76,6 +79,7 @@ class ArtistChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.oxycblt.auxio.ui.NavigationViewModel
|
|||
|
||||
/**
|
||||
* An [ArtistPickerDialog] intended for when [Artist] navigation is ambiguous.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.util.collectImmediately
|
|||
* The base class for dialogs that implements common behavior across all [Artist] pickers. These are
|
||||
* shown whenever what to do with an item's [Artist] is ambiguous, as there are multiple [Artist]'s
|
||||
* to choose from.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* An [ArtistPickerDialog] intended for when [Artist] playback is ambiguous.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* An [RecyclerView.Adapter] that displays a list of [Genre] choices.
|
||||
*
|
||||
* @param listener A [ClickableListListener] to bind interactions to.
|
||||
* @author OxygenCobalt.
|
||||
*/
|
||||
|
@ -46,6 +47,7 @@ class GenreChoiceAdapter(private val listener: ClickableListListener<Genre>) :
|
|||
|
||||
/**
|
||||
* Immediately update the [Genre] choices.
|
||||
*
|
||||
* @param newGenres The new [Genre]s to show.
|
||||
*/
|
||||
fun submitList(newGenres: List<Genre>) {
|
||||
|
@ -64,6 +66,7 @@ class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|||
DialogRecyclerView.ViewHolder(binding.root) {
|
||||
/**
|
||||
* Bind new data to this instance.
|
||||
*
|
||||
* @param genre The new [Genre] to bind.
|
||||
* @param listener A [ClickableListListener] to bind interactions to.
|
||||
*/
|
||||
|
@ -76,6 +79,7 @@ class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param parent The parent to inflate this instance from.
|
||||
* @return A new instance.
|
||||
*/
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
|
||||
/**
|
||||
* A picker [ViewBindingDialogFragment] intended for when [Genre] playback is ambiguous.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
|||
/**
|
||||
* a [ViewModel] that manages the current music picker state. Make it so that the dialogs just
|
||||
* contain the music themselves and then exit if the library changes.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -62,6 +63,7 @@ class PickerViewModel @Inject constructor(private val musicRepository: MusicRepo
|
|||
|
||||
/**
|
||||
* Set a new [currentItem] from it's [Music.UID].
|
||||
*
|
||||
* @param uid The [Music.UID] of the [Song] to update to.
|
||||
*/
|
||||
fun setItemUid(uid: Music.UID) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.oxycblt.auxio.IntegerTable
|
|||
/**
|
||||
* Represents a configuration option for what kind of "secondary" action to show in a particular UI
|
||||
* context.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
enum class ActionMode {
|
||||
|
@ -34,6 +35,7 @@ enum class ActionMode {
|
|||
|
||||
/**
|
||||
* The integer representation of this instance.
|
||||
*
|
||||
* @see fromIntCode
|
||||
*/
|
||||
val intCode: Int
|
||||
|
@ -47,6 +49,7 @@ enum class ActionMode {
|
|||
companion object {
|
||||
/**
|
||||
* Convert a [ActionMode] integer representation into an instance.
|
||||
*
|
||||
* @param intCode An integer representation of a [ActionMode]
|
||||
* @return The corresponding [ActionMode], or null if the [ActionMode] is invalid.
|
||||
* @see ActionMode.intCode
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.util.getColorCompat
|
|||
|
||||
/**
|
||||
* A [ViewBindingFragment] that shows the current playback state in a compact manner.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.getDimen
|
|||
|
||||
/**
|
||||
* The [BaseBottomSheetBehavior] for the playback bottom sheet. This bottom sheet
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class PlaybackBottomSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
/**
|
||||
* A [ViewBindingFragment] more information about the currently playing song, alongside all
|
||||
* available controls.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.util.logD
|
|||
|
||||
/**
|
||||
* User configuration specific to the playback system.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface PlaybackSettings : Settings<PlaybackSettings.Listener> {
|
||||
|
|
|
@ -21,36 +21,42 @@ import android.text.format.DateUtils
|
|||
|
||||
/**
|
||||
* Convert milliseconds into deci-seconds (1/10th of a second).
|
||||
*
|
||||
* @return A converted deci-second value.
|
||||
*/
|
||||
fun Long.msToDs() = floorDiv(100)
|
||||
|
||||
/**
|
||||
* Convert milliseconds into seconds.
|
||||
*
|
||||
* @return A converted second value.
|
||||
*/
|
||||
fun Long.msToSecs() = floorDiv(1000)
|
||||
|
||||
/**
|
||||
* Convert deci-seconds (1/10th of a second) into milliseconds.
|
||||
*
|
||||
* @return A converted millisecond value.
|
||||
*/
|
||||
fun Long.dsToMs() = times(100)
|
||||
|
||||
/**
|
||||
* Convert deci-seconds (1/10th of a second) into seconds.
|
||||
*
|
||||
* @return A converted second value.
|
||||
*/
|
||||
fun Long.dsToSecs() = floorDiv(10)
|
||||
|
||||
/**
|
||||
* Convert seconds into milliseconds.
|
||||
*
|
||||
* @return A converted millisecond value.
|
||||
*/
|
||||
fun Long.secsToMs() = times(1000)
|
||||
|
||||
/**
|
||||
* Convert a millisecond value into a string duration.
|
||||
*
|
||||
* @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:--
|
||||
* will be returned if the second value is 0.
|
||||
*/
|
||||
|
@ -58,6 +64,7 @@ fun Long.formatDurationMs(isElapsed: Boolean) = msToSecs().formatDurationSecs(is
|
|||
|
||||
/**
|
||||
* // * Format a deci-second value (1/10th of a second) into a string duration.
|
||||
*
|
||||
* @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:--
|
||||
* will be returned if the second value is 0.
|
||||
*/
|
||||
|
@ -65,6 +72,7 @@ fun Long.formatDurationDs(isElapsed: Boolean) = dsToSecs().formatDurationSecs(is
|
|||
|
||||
/**
|
||||
* Convert a second value into a string duration.
|
||||
*
|
||||
* @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:--
|
||||
* will be returned if the second value is 0.
|
||||
*/
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.oxycblt.auxio.playback.state.*
|
|||
|
||||
/**
|
||||
* An [ViewModel] that provides a safe UI frontend for the current playback state.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
|
@ -76,6 +77,7 @@ constructor(
|
|||
/**
|
||||
* Flag signaling to open a picker dialog in order to resolve an ambiguous choice when playing a
|
||||
* [Song] from one of it's [Artist]s.
|
||||
*
|
||||
* @see playFromArtist
|
||||
*/
|
||||
val artistPickerSong: StateFlow<Song?>
|
||||
|
@ -163,6 +165,7 @@ constructor(
|
|||
* - If [MusicMode.ALBUMS], the [Song] is played from it's [Album].
|
||||
* - If [MusicMode.ARTISTS], the [Song] is played from one of it's [Artist]s.
|
||||
* - If [MusicMode.GENRES], the [Song] is played from one of it's [Genre]s.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param playbackMode The [MusicMode] to play from.
|
||||
*/
|
||||
|
@ -177,6 +180,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Play a [Song] from one of it's [Artist]s.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param artist The [Artist] to play from. Must be linked to the [Song]. If null, the user will
|
||||
* be prompted on what artist to play. Defaults to null.
|
||||
|
@ -194,6 +198,7 @@ constructor(
|
|||
/**
|
||||
* Mark the [Artist] playback choice process as complete. This should occur when the [Artist]
|
||||
* choice dialog is opened after this flag is detected.
|
||||
*
|
||||
* @see playFromArtist
|
||||
*/
|
||||
fun finishPlaybackArtistPicker() {
|
||||
|
@ -202,6 +207,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* PLay a [Song] from one of it's [Genre]s.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param genre The [Genre] to play from. Must be linked to the [Song]. If null, the user will
|
||||
* be prompted on what artist to play. Defaults to null.
|
||||
|
@ -219,6 +225,7 @@ constructor(
|
|||
/**
|
||||
* Mark the [Genre] playback choice process as complete. This should occur when the [Genre]
|
||||
* choice dialog is opened after this flag is detected.
|
||||
*
|
||||
* @see playFromGenre
|
||||
*/
|
||||
fun finishPlaybackGenrePicker() {
|
||||
|
@ -227,24 +234,28 @@ constructor(
|
|||
|
||||
/**
|
||||
* Play an [Album].
|
||||
*
|
||||
* @param album The [Album] to play.
|
||||
*/
|
||||
fun play(album: Album) = playImpl(null, album, false)
|
||||
|
||||
/**
|
||||
* Play an [Artist].
|
||||
*
|
||||
* @param artist The [Artist] to play.
|
||||
*/
|
||||
fun play(artist: Artist) = playImpl(null, artist, false)
|
||||
|
||||
/**
|
||||
* Play a [Genre].
|
||||
*
|
||||
* @param genre The [Genre] to play.
|
||||
*/
|
||||
fun play(genre: Genre) = playImpl(null, genre, false)
|
||||
|
||||
/**
|
||||
* Play a [Music] selection.
|
||||
*
|
||||
* @param selection The selection to play.
|
||||
*/
|
||||
fun play(selection: List<Music>) =
|
||||
|
@ -252,24 +263,28 @@ constructor(
|
|||
|
||||
/**
|
||||
* Shuffle an [Album].
|
||||
*
|
||||
* @param album The [Album] to shuffle.
|
||||
*/
|
||||
fun shuffle(album: Album) = playImpl(null, album, true)
|
||||
|
||||
/**
|
||||
* Shuffle an [Artist].
|
||||
*
|
||||
* @param artist The [Artist] to shuffle.
|
||||
*/
|
||||
fun shuffle(artist: Artist) = playImpl(null, artist, true)
|
||||
|
||||
/**
|
||||
* Shuffle an [Genre].
|
||||
*
|
||||
* @param genre The [Genre] to shuffle.
|
||||
*/
|
||||
fun shuffle(genre: Genre) = playImpl(null, genre, true)
|
||||
|
||||
/**
|
||||
* Shuffle a [Music] selection.
|
||||
*
|
||||
* @param selection The selection to shuffle.
|
||||
*/
|
||||
fun shuffle(selection: List<Music>) =
|
||||
|
@ -298,6 +313,7 @@ constructor(
|
|||
/**
|
||||
* Start the given [InternalPlayer.Action] to be completed eventually. This can be used to
|
||||
* enqueue a playback action at startup to then occur when the music library is fully loaded.
|
||||
*
|
||||
* @param action The [InternalPlayer.Action] to perform eventually.
|
||||
*/
|
||||
fun startAction(action: InternalPlayer.Action) {
|
||||
|
@ -308,6 +324,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Seek to the given position in the currently playing [Song].
|
||||
*
|
||||
* @param positionDs The position to seek to, in deci-seconds (1/10th of a second).
|
||||
*/
|
||||
fun seekTo(positionDs: Long) {
|
||||
|
@ -328,6 +345,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Song] to the top of the queue.
|
||||
*
|
||||
* @param song The [Song] to add.
|
||||
*/
|
||||
fun playNext(song: Song) {
|
||||
|
@ -336,6 +354,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Album] to the top of the queue.
|
||||
*
|
||||
* @param album The [Album] to add.
|
||||
*/
|
||||
fun playNext(album: Album) {
|
||||
|
@ -344,6 +363,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Artist] to the top of the queue.
|
||||
*
|
||||
* @param artist The [Artist] to add.
|
||||
*/
|
||||
fun playNext(artist: Artist) {
|
||||
|
@ -352,6 +372,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Genre] to the top of the queue.
|
||||
*
|
||||
* @param genre The [Genre] to add.
|
||||
*/
|
||||
fun playNext(genre: Genre) {
|
||||
|
@ -360,6 +381,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a selection to the top of the queue.
|
||||
*
|
||||
* @param selection The [Music] selection to add.
|
||||
*/
|
||||
fun playNext(selection: List<Music>) {
|
||||
|
@ -368,6 +390,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Song] to the end of the queue.
|
||||
*
|
||||
* @param song The [Song] to add.
|
||||
*/
|
||||
fun addToQueue(song: Song) {
|
||||
|
@ -376,6 +399,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Album] to the end of the queue.
|
||||
*
|
||||
* @param album The [Album] to add.
|
||||
*/
|
||||
fun addToQueue(album: Album) {
|
||||
|
@ -384,6 +408,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Artist] to the end of the queue.
|
||||
*
|
||||
* @param artist The [Artist] to add.
|
||||
*/
|
||||
fun addToQueue(artist: Artist) {
|
||||
|
@ -392,6 +417,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a [Genre] to the end of the queue.
|
||||
*
|
||||
* @param genre The [Genre] to add.
|
||||
*/
|
||||
fun addToQueue(genre: Genre) {
|
||||
|
@ -400,6 +426,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Add a selection to the end of the queue.
|
||||
*
|
||||
* @param selection The [Music] selection to add.
|
||||
*/
|
||||
fun addToQueue(selection: List<Music>) {
|
||||
|
@ -420,6 +447,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Toggle [repeatMode] (ex. from [RepeatMode.NONE] to [RepeatMode.TRACK])
|
||||
*
|
||||
* @see RepeatMode.increment
|
||||
*/
|
||||
fun toggleRepeatMode() {
|
||||
|
@ -430,6 +458,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Force-save the current playback state.
|
||||
*
|
||||
* @param onDone Called when the save is completed with true if successful, and false otherwise.
|
||||
*/
|
||||
fun savePlaybackState(onDone: (Boolean) -> Unit) {
|
||||
|
@ -440,6 +469,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Clear the current playback state.
|
||||
*
|
||||
* @param onDone Called when the wipe is completed with true if successful, and false otherwise.
|
||||
*/
|
||||
fun wipePlaybackState(onDone: (Boolean) -> Unit) {
|
||||
|
@ -448,6 +478,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Force-restore the current playback state.
|
||||
*
|
||||
* @param onDone Called when the restoration is completed with true if successful, and false
|
||||
* otherwise.
|
||||
*/
|
||||
|
@ -468,6 +499,7 @@ constructor(
|
|||
|
||||
/**
|
||||
* Convert the given selection to a list of [Song]s.
|
||||
*
|
||||
* @param selection The selection of [Music] to convert.
|
||||
* @return A [Song] list containing the child items of any [MusicParent] instances in the list
|
||||
* alongside the unchanged [Song]s or the original selection.
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.oxycblt.auxio.playback.state.RepeatMode
|
|||
|
||||
/**
|
||||
* Provides raw access to the database storing the persisted playback state.
|
||||
*
|
||||
* @author Alexander Capehart
|
||||
*/
|
||||
@Database(
|
||||
|
@ -42,12 +43,14 @@ import org.oxycblt.auxio.playback.state.RepeatMode
|
|||
abstract class PersistenceDatabase : RoomDatabase() {
|
||||
/**
|
||||
* Get the current [PlaybackStateDao].
|
||||
*
|
||||
* @return A [PlaybackStateDao] providing control of the database's playback state tables.
|
||||
*/
|
||||
abstract fun playbackStateDao(): PlaybackStateDao
|
||||
|
||||
/**
|
||||
* Get the current [QueueDao].
|
||||
*
|
||||
* @return A [QueueDao] providing control of the database's queue tables.
|
||||
*/
|
||||
abstract fun queueDao(): QueueDao
|
||||
|
@ -63,12 +66,14 @@ abstract class PersistenceDatabase : RoomDatabase() {
|
|||
|
||||
/**
|
||||
* Provides control of the persisted playback state table.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@Dao
|
||||
interface PlaybackStateDao {
|
||||
/**
|
||||
* Get the previously persisted [PlaybackState].
|
||||
*
|
||||
* @return The previously persisted [PlaybackState], or null if one was not present.
|
||||
*/
|
||||
@Query("SELECT * FROM ${PlaybackState.TABLE_NAME} WHERE id = 0")
|
||||
|
@ -79,6 +84,7 @@ interface PlaybackStateDao {
|
|||
|
||||
/**
|
||||
* Insert a new [PlaybackState] into the database.
|
||||
*
|
||||
* @param state The [PlaybackState] to insert.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insertState(state: PlaybackState)
|
||||
|
@ -86,18 +92,21 @@ interface PlaybackStateDao {
|
|||
|
||||
/**
|
||||
* Provides control of the persisted queue state tables.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@Dao
|
||||
interface QueueDao {
|
||||
/**
|
||||
* Get the previously persisted queue heap.
|
||||
*
|
||||
* @return A list of persisted [QueueHeapItem]s wrapping each heap item.
|
||||
*/
|
||||
@Query("SELECT * FROM ${QueueHeapItem.TABLE_NAME}") suspend fun getHeap(): List<QueueHeapItem>
|
||||
|
||||
/**
|
||||
* Get the previously persisted queue mapping.
|
||||
*
|
||||
* @return A list of persisted [QueueMappingItem]s wrapping each heap item.
|
||||
*/
|
||||
@Query("SELECT * FROM ${QueueMappingItem.TABLE_NAME}")
|
||||
|
@ -111,12 +120,14 @@ interface QueueDao {
|
|||
|
||||
/**
|
||||
* Insert new heap entries into the database.
|
||||
*
|
||||
* @param heap The list of wrapped [QueueHeapItem]s to insert.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insertHeap(heap: List<QueueHeapItem>)
|
||||
|
||||
/**
|
||||
* Insert new mapping entries into the database.
|
||||
*
|
||||
* @param mapping The list of wrapped [QueueMappingItem] to insert.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
|
|
|
@ -27,17 +27,20 @@ import org.oxycblt.auxio.util.logE
|
|||
|
||||
/**
|
||||
* Manages the persisted playback state in a structured manner.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
interface PersistenceRepository {
|
||||
/**
|
||||
* Read the previously persisted [PlaybackStateManager.SavedState].
|
||||
*
|
||||
* @param library The [Library] required to de-serialize the [PlaybackStateManager.SavedState].
|
||||
*/
|
||||
suspend fun readState(library: Library): PlaybackStateManager.SavedState?
|
||||
|
||||
/**
|
||||
* Persist a new [PlaybackStateManager.SavedState].
|
||||
*
|
||||
* @param state The [PlaybackStateManager.SavedState] to persist.
|
||||
*/
|
||||
suspend fun saveState(state: PlaybackStateManager.SavedState?): Boolean
|
||||
|
|
|
@ -45,6 +45,7 @@ interface Queue {
|
|||
val isShuffled: Boolean
|
||||
/**
|
||||
* Resolve this queue into a more conventional list of [Song]s.
|
||||
*
|
||||
* @return A list of [Song] corresponding to the current queue mapping.
|
||||
*/
|
||||
fun resolve(): List<Song>
|
||||
|
@ -67,6 +68,7 @@ interface Queue {
|
|||
|
||||
/**
|
||||
* An immutable representation of the queue state.
|
||||
*
|
||||
* @param heap The heap of [Song]s that are/were used in the queue. This can be modified with
|
||||
* null values to represent [Song]s that were "lost" from the heap without having to change
|
||||
* other values.
|
||||
|
@ -85,6 +87,7 @@ interface Queue {
|
|||
/**
|
||||
* Remaps the [heap] of this instance based on the given mapping function and copies it into
|
||||
* a new [SavedState].
|
||||
*
|
||||
* @param transform Code to remap the existing [Song] heap into a new [Song] heap. This
|
||||
* **MUST** be the same size as the original heap. [Song] instances that could not be
|
||||
* converted should be replaced with null in the new heap.
|
||||
|
@ -121,6 +124,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Go to a particular index in the queue.
|
||||
*
|
||||
* @param to The index of the [Song] to start playing, in the current queue mapping.
|
||||
* @return true if the queue jumped to that position, false otherwise.
|
||||
*/
|
||||
|
@ -134,6 +138,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Start a new queue configuration.
|
||||
*
|
||||
* @param play The [Song] to play, or null to start from a random position.
|
||||
* @param queue The queue of [Song]s to play. Must contain [play]. This list will become the
|
||||
* heap internally.
|
||||
|
@ -152,6 +157,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Re-order the queue.
|
||||
*
|
||||
* @param shuffled Whether the queue should be shuffled or not.
|
||||
*/
|
||||
fun reorder(shuffled: Boolean) {
|
||||
|
@ -185,10 +191,11 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Add [Song]s to the top of the queue. Will start playback if nothing is playing.
|
||||
*
|
||||
* @param songs The [Song]s to add.
|
||||
* @return [Queue.ChangeResult.MAPPING] if added to an existing queue, or
|
||||
* [Queue.ChangeResult.SONG] if there was no prior playback and these enqueued [Song]s start new
|
||||
* playback.
|
||||
* [Queue.ChangeResult.SONG] if there was no prior playback and these enqueued [Song]s start
|
||||
* new playback.
|
||||
*/
|
||||
fun playNext(songs: List<Song>): Queue.ChangeResult {
|
||||
if (orderedMapping.isEmpty()) {
|
||||
|
@ -214,10 +221,11 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Add [Song]s to the end of the queue. Will start playback if nothing is playing.
|
||||
*
|
||||
* @param songs The [Song]s to add.
|
||||
* @return [Queue.ChangeResult.MAPPING] if added to an existing queue, or
|
||||
* [Queue.ChangeResult.SONG] if there was no prior playback and these enqueued [Song]s start new
|
||||
* playback.
|
||||
* [Queue.ChangeResult.SONG] if there was no prior playback and these enqueued [Song]s start
|
||||
* new playback.
|
||||
*/
|
||||
fun addToQueue(songs: List<Song>): Queue.ChangeResult {
|
||||
if (orderedMapping.isEmpty()) {
|
||||
|
@ -238,11 +246,12 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Move a [Song] at the given position to a new position.
|
||||
*
|
||||
* @param src The position of the [Song] to move.
|
||||
* @param dst The destination position of the [Song].
|
||||
* @return [Queue.ChangeResult.MAPPING] if the move occurred after the current index,
|
||||
* [Queue.ChangeResult.INDEX] if the move occurred before or at the current index, requiring it
|
||||
* to be mutated.
|
||||
* [Queue.ChangeResult.INDEX] if the move occurred before or at the current index, requiring
|
||||
* it to be mutated.
|
||||
*/
|
||||
fun move(src: Int, dst: Int): Queue.ChangeResult {
|
||||
if (shuffledMapping.isNotEmpty()) {
|
||||
|
@ -273,6 +282,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Remove a [Song] at the given position.
|
||||
*
|
||||
* @param at The position of the [Song] to remove.
|
||||
* @return [Queue.ChangeResult.MAPPING] if the removed [Song] was after the current index,
|
||||
* [Queue.ChangeResult.INDEX] if the removed [Song] was before the current index, and
|
||||
|
@ -311,6 +321,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Convert the current state of this instance into a [Queue.SavedState].
|
||||
*
|
||||
* @return A new [Queue.SavedState] reflecting the exact state of the queue when called.
|
||||
*/
|
||||
fun toSavedState() =
|
||||
|
@ -321,6 +332,7 @@ class EditableQueue : Queue {
|
|||
|
||||
/**
|
||||
* Update this instance from the given [Queue.SavedState].
|
||||
*
|
||||
* @param savedState A [Queue.SavedState] with a valid queue representation.
|
||||
*/
|
||||
fun applySavedState(savedState: Queue.SavedState) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue