all: reformat code
Simultaniously reformat code using ktlint and ktfmt.
This commit is contained in:
parent
28d28287fe
commit
09823d7829
55 changed files with 385 additions and 236 deletions
|
@ -120,7 +120,14 @@ dependencies {
|
|||
spotless {
|
||||
kotlin {
|
||||
target "src/**/*.kt"
|
||||
ktfmt("0.37").dropboxStyle()
|
||||
|
||||
// ktlint does checking, while ktfmt actually does formatting
|
||||
ktlint()
|
||||
ktfmt().dropboxStyle()
|
||||
licenseHeaderFile("NOTICE")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
preDebugBuild.dependsOn spotlessApply
|
||||
}
|
|
@ -21,51 +21,70 @@ package org.oxycblt.auxio
|
|||
object IntegerTable {
|
||||
/** SongViewHolder */
|
||||
const val VIEW_TYPE_SONG = 0xA000
|
||||
|
||||
/** AlbumViewHolder */
|
||||
const val VIEW_TYPE_ALBUM = 0xA001
|
||||
|
||||
/** ArtistViewHolder */
|
||||
const val VIEW_TYPE_ARTIST = 0xA002
|
||||
|
||||
/** GenreViewHolder */
|
||||
const val VIEW_TYPE_GENRE = 0xA003
|
||||
|
||||
/** HeaderViewHolder */
|
||||
const val VIEW_TYPE_HEADER = 0xA004
|
||||
|
||||
/** SortHeaderViewHolder */
|
||||
const val VIEW_TYPE_SORT_HEADER = 0xA005
|
||||
|
||||
/** AlbumDetailViewHolder */
|
||||
const val VIEW_TYPE_ALBUM_DETAIL = 0xA006
|
||||
|
||||
/** AlbumSongViewHolder */
|
||||
const val VIEW_TYPE_ALBUM_SONG = 0xA007
|
||||
|
||||
/** ArtistDetailViewHolder */
|
||||
const val VIEW_TYPE_ARTIST_DETAIL = 0xA008
|
||||
|
||||
/** ArtistAlbumViewHolder */
|
||||
const val VIEW_TYPE_ARTIST_ALBUM = 0xA009
|
||||
|
||||
/** ArtistSongViewHolder */
|
||||
const val VIEW_TYPE_ARTIST_SONG = 0xA00A
|
||||
|
||||
/** GenreDetailViewHolder */
|
||||
const val VIEW_TYPE_GENRE_DETAIL = 0xA00B
|
||||
|
||||
/** DiscHeaderViewHolder */
|
||||
const val VIEW_TYPE_DISC_HEADER = 0xA00C
|
||||
|
||||
/** "Music playback" notification code */
|
||||
const val PLAYBACK_NOTIFICATION_CODE = 0xA0A0
|
||||
|
||||
/** "Music loading" notification code */
|
||||
const val INDEXER_NOTIFICATION_CODE = 0xA0A1
|
||||
|
||||
/** Intent request code */
|
||||
const val REQUEST_CODE = 0xA0C0
|
||||
|
||||
/** RepeatMode.NONE */
|
||||
const val REPEAT_MODE_NONE = 0xA100
|
||||
|
||||
/** RepeatMode.ALL */
|
||||
const val REPEAT_MODE_ALL = 0xA101
|
||||
|
||||
/** RepeatMode.TRACK */
|
||||
const val REPEAT_MODE_TRACK = 0xA102
|
||||
|
||||
/** PlaybackMode.IN_GENRE */
|
||||
const val PLAYBACK_MODE_IN_GENRE = 0xA103
|
||||
|
||||
/** PlaybackMode.IN_ARTIST */
|
||||
const val PLAYBACK_MODE_IN_ARTIST = 0xA104
|
||||
|
||||
/** PlaybackMode.IN_ALBUM */
|
||||
const val PLAYBACK_MODE_IN_ALBUM = 0xA105
|
||||
|
||||
/** PlaybackMode.ALL_SONGS */
|
||||
const val PLAYBACK_MODE_ALL_SONGS = 0xA106
|
||||
|
||||
|
@ -73,10 +92,13 @@ object IntegerTable {
|
|||
// const val DISPLAY_MODE_NONE = 0xA107
|
||||
/** DisplayMode.SHOW_GENRES */
|
||||
const val DISPLAY_MODE_SHOW_GENRES = 0xA108
|
||||
|
||||
/** DisplayMode.SHOW_ARTISTS */
|
||||
const val DISPLAY_MODE_SHOW_ARTISTS = 0xA109
|
||||
|
||||
/** DisplayMode.SHOW_ALBUMS */
|
||||
const val DISPLAY_MODE_SHOW_ALBUMS = 0xA10A
|
||||
|
||||
/** DisplayMode.SHOW_SONGS */
|
||||
const val DISPLAY_MODE_SHOW_SONGS = 0xA10B
|
||||
|
||||
|
@ -85,20 +107,28 @@ object IntegerTable {
|
|||
|
||||
/** Sort.ByName */
|
||||
const val SORT_BY_NAME = 0xA10C
|
||||
|
||||
/** Sort.ByArtist */
|
||||
const val SORT_BY_ARTIST = 0xA10D
|
||||
|
||||
/** Sort.ByAlbum */
|
||||
const val SORT_BY_ALBUM = 0xA10E
|
||||
|
||||
/** Sort.ByYear */
|
||||
const val SORT_BY_YEAR = 0xA10F
|
||||
|
||||
/** Sort.ByDuration */
|
||||
const val SORT_BY_DURATION = 0xA114
|
||||
|
||||
/** Sort.ByCount */
|
||||
const val SORT_BY_COUNT = 0xA115
|
||||
|
||||
/** Sort.ByDisc */
|
||||
const val SORT_BY_DISC = 0xA116
|
||||
|
||||
/** Sort.ByTrack */
|
||||
const val SORT_BY_TRACK = 0xA117
|
||||
|
||||
/** Sort.ByDateAdded */
|
||||
const val SORT_BY_DATE_ADDED = 0xA118
|
||||
|
||||
|
@ -106,15 +136,19 @@ object IntegerTable {
|
|||
// const val REPLAY_GAIN_MODE_OFF = 0xA110
|
||||
/** ReplayGainMode.Track */
|
||||
const val REPLAY_GAIN_MODE_TRACK = 0xA111
|
||||
|
||||
/** ReplayGainMode.Album */
|
||||
const val REPLAY_GAIN_MODE_ALBUM = 0xA112
|
||||
|
||||
/** ReplayGainMode.Dynamic */
|
||||
const val REPLAY_GAIN_MODE_DYNAMIC = 0xA113
|
||||
|
||||
/** BarAction.Next */
|
||||
const val BAR_ACTION_NEXT = 0xA119
|
||||
|
||||
/** BarAction.Repeat */
|
||||
const val BAR_ACTION_REPEAT = 0xA11A
|
||||
|
||||
/** BarAction.Shuffle */
|
||||
const val BAR_ACTION_SHUFFLE = 0xA11B
|
||||
}
|
||||
|
|
|
@ -42,7 +42,15 @@ import org.oxycblt.auxio.playback.queue.QueueSheetBehavior
|
|||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.*
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
* A wrapper around the home fragment that shows the playback fragment and controls the more
|
||||
|
|
|
@ -28,7 +28,14 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.*
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MimeType
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.ReleaseType
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
|
|
|
@ -26,10 +26,10 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogSongDetailBinding
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
|
||||
/**
|
||||
* A dialog displayed when "View properties" is selected on a song, showing more information about
|
||||
|
|
|
@ -29,12 +29,12 @@ import org.oxycblt.auxio.databinding.ItemDiscHeaderBinding
|
|||
import org.oxycblt.auxio.detail.DiscHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
|
|
|
@ -143,10 +143,8 @@ private class ArtistDetailViewHolder private constructor(private val binding: It
|
|||
}
|
||||
}
|
||||
|
||||
private class ArtistAlbumViewHolder
|
||||
private constructor(
|
||||
private val binding: ItemParentBinding,
|
||||
) : IndicatorAdapter.ViewHolder(binding.root) {
|
||||
private class ArtistAlbumViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
IndicatorAdapter.ViewHolder(binding.root) {
|
||||
fun bind(item: Album, listener: MenuItemListener) {
|
||||
binding.parentImage.bind(item)
|
||||
binding.parentName.text = item.resolveName(binding.context)
|
||||
|
@ -178,10 +176,8 @@ private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private class ArtistSongViewHolder
|
||||
private constructor(
|
||||
private val binding: ItemSongBinding,
|
||||
) : IndicatorAdapter.ViewHolder(binding.root) {
|
||||
private class ArtistSongViewHolder private constructor(private val binding: ItemSongBinding) :
|
||||
IndicatorAdapter.ViewHolder(binding.root) {
|
||||
fun bind(item: Song, listener: MenuItemListener) {
|
||||
binding.songAlbumCover.bind(item)
|
||||
binding.songName.text = item.resolveName(binding.context)
|
||||
|
|
|
@ -83,8 +83,7 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
return item is Header || item is SortHeader
|
||||
}
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
protected val differ = AsyncListDiffer(this, diffCallback)
|
||||
@Suppress("LeakingThis") protected val differ = AsyncListDiffer(this, diffCallback)
|
||||
|
||||
override val currentList: List<Item>
|
||||
get() = differ.currentList
|
||||
|
|
|
@ -25,11 +25,11 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.util.getPlural
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
|
|
|
@ -45,7 +45,12 @@ import org.oxycblt.auxio.home.list.AlbumListFragment
|
|||
import org.oxycblt.auxio.home.list.ArtistListFragment
|
||||
import org.oxycblt.auxio.home.list.GenreListFragment
|
||||
import org.oxycblt.auxio.home.list.SongListFragment
|
||||
import org.oxycblt.auxio.music.*
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.system.Indexer
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
|
@ -53,7 +58,12 @@ import org.oxycblt.auxio.ui.MainNavigationAction
|
|||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.*
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
import org.oxycblt.auxio.util.lazyReflectedField
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail views for each
|
||||
|
|
|
@ -21,11 +21,13 @@ import android.os.Bundle
|
|||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import java.util.*
|
||||
import java.util.Formatter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
||||
|
@ -34,8 +36,6 @@ import org.oxycblt.auxio.ui.recycler.Item
|
|||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SyncListDiffer
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
|
||||
/**
|
||||
* A [HomeListFragment] for showing a list of [Album]s.
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
||||
|
@ -32,7 +33,6 @@ import org.oxycblt.auxio.ui.recycler.Item
|
|||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SyncListDiffer
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
|
||||
/**
|
||||
* A [HomeListFragment] for showing a list of [Artist]s.
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
|
||||
|
@ -32,7 +33,6 @@ import org.oxycblt.auxio.ui.recycler.Item
|
|||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SyncListDiffer
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
|
||||
/**
|
||||
* A [HomeListFragment] for showing a list of [Genre]s.
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
|
@ -36,8 +38,6 @@ import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
|||
import org.oxycblt.auxio.ui.recycler.SyncListDiffer
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.music.secsToMs
|
||||
|
||||
/**
|
||||
* A [HomeListFragment] for showing a list of [Song]s.
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
package org.oxycblt.auxio.home.tabs
|
||||
|
||||
import org.oxycblt.auxio.home.tabs.Tab.Companion.fromSequence
|
||||
import org.oxycblt.auxio.home.tabs.Tab.Companion.toSequence
|
||||
import org.oxycblt.auxio.home.tabs.Tab.Invisible
|
||||
import org.oxycblt.auxio.home.tabs.Tab.Visible
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
||||
|
@ -50,6 +54,7 @@ sealed class Tab(open val mode: DisplayMode) {
|
|||
companion object {
|
||||
/** The length a well-formed tab sequence should be */
|
||||
private const val SEQUENCE_LEN = 4
|
||||
|
||||
/** The default tab sequence, represented in integer form */
|
||||
const val SEQUENCE_DEFAULT = 0b1000_1001_1010_1011_0100
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ class ArtistImageFetcher
|
|||
private constructor(
|
||||
private val context: Context,
|
||||
private val size: Size,
|
||||
private val artist: Artist,
|
||||
private val artist: Artist
|
||||
) : BaseFetcher() {
|
||||
override suspend fun fetch(): FetchResult? {
|
||||
val albums = Sort(Sort.Mode.ByName, true).albums(artist.albums)
|
||||
|
@ -104,7 +104,7 @@ class GenreImageFetcher
|
|||
private constructor(
|
||||
private val context: Context,
|
||||
private val size: Size,
|
||||
private val genre: Genre,
|
||||
private val genre: Genre
|
||||
) : BaseFetcher() {
|
||||
override suspend fun fetch(): FetchResult? {
|
||||
// Genre logic is the most complicated, as we want to ensure album cover variation (i.e
|
||||
|
|
|
@ -30,6 +30,7 @@ import kotlinx.parcelize.IgnoredOnParcel
|
|||
import kotlinx.parcelize.Parcelize
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Date.Companion.from
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.util.inRangeOrNull
|
||||
|
@ -53,7 +54,9 @@ sealed class Music : Item {
|
|||
* fast-scrolling.
|
||||
*/
|
||||
val sortName: String?
|
||||
get() = rawSortName ?: rawName?.run {
|
||||
get() =
|
||||
rawSortName
|
||||
?: rawName?.run {
|
||||
when {
|
||||
length > 5 && startsWith("the ", ignoreCase = true) -> substring(4)
|
||||
length > 4 && startsWith("an ", ignoreCase = true) -> substring(3)
|
||||
|
@ -185,6 +188,7 @@ class Song constructor(raw: Raw) : Music() {
|
|||
val disc = raw.disc
|
||||
|
||||
private var _album: Album? = null
|
||||
|
||||
/** The album of this song. */
|
||||
val album: Album
|
||||
get() = unlikelyToBeNull(_album)
|
||||
|
@ -212,6 +216,7 @@ class Song constructor(raw: Raw) : Music() {
|
|||
artistName ?: album.artist.resolveName(context)
|
||||
|
||||
private val _genres: MutableList<Genre> = mutableListOf()
|
||||
|
||||
/**
|
||||
* The genres of this song. Most often one, but there could be multiple. There will always be at
|
||||
* least one genre, even if it is an "unknown genre" instance.
|
||||
|
@ -327,6 +332,7 @@ class Album constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
|||
val durationMs = songs.sumOf { it.durationMs }
|
||||
|
||||
private var _artist: Artist? = null
|
||||
|
||||
/** The parent artist of this album. */
|
||||
val artist: Artist
|
||||
get() = unlikelyToBeNull(_artist)
|
||||
|
@ -634,9 +640,8 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
fun from(timestamp: String): Date? {
|
||||
val groups =
|
||||
(ISO8601_REGEX.matchEntire(timestamp) ?: return null)
|
||||
.groupValues.mapIndexedNotNull { index, s ->
|
||||
if (index % 2 != 0) s.toIntOrNull() else null
|
||||
}
|
||||
.groupValues
|
||||
.mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null }
|
||||
|
||||
return fromTokens(groups)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.oxycblt.auxio.music
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import org.oxycblt.auxio.music.MusicStore.Callback
|
||||
import org.oxycblt.auxio.music.MusicStore.Library
|
||||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
|
||||
/**
|
||||
|
@ -99,12 +101,16 @@ class MusicStore private constructor() {
|
|||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(song: Song) = find<Song>(song.uid)
|
||||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(songs: List<Song>) = songs.mapNotNull { sanitize(it) }
|
||||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(album: Album) = find<Album>(album.uid)
|
||||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(artist: Artist) = find<Artist>(artist.uid)
|
||||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(genre: Genre) = find<Genre>(genre.uid)
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ import android.database.Cursor
|
|||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import android.text.format.DateUtils
|
||||
import java.util.UUID
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import java.util.UUID
|
||||
|
||||
/** Shortcut for making a [ContentResolver] query with less superfluous arguments. */
|
||||
fun ContentResolver.queryCursor(
|
||||
|
@ -59,13 +59,17 @@ val Long.audioUri: Uri
|
|||
val Long.albumCoverUri: Uri
|
||||
get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this)
|
||||
|
||||
|
||||
/** Shortcut to resolve a year from a nullable date. Will return "No Date" if it is null. */
|
||||
fun Date?.resolveYear(context: Context) =
|
||||
this?.resolveYear(context) ?: context.getString(R.string.def_date)
|
||||
|
||||
/** Converts this string to a UUID, or returns null if it is not valid. */
|
||||
fun String.toUuid() = try { UUID.fromString(this) } catch (e: IllegalArgumentException) { null }
|
||||
fun String.toUuid() =
|
||||
try {
|
||||
UUID.fromString(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
|
||||
/** Converts a long in milliseconds to a long in deci-seconds */
|
||||
fun Long.msToDs() = floorDiv(100)
|
||||
|
|
|
@ -30,10 +30,12 @@ class MusicViewModel : ViewModel(), Indexer.Callback {
|
|||
private val indexer = Indexer.getInstance()
|
||||
|
||||
private val _indexerState = MutableStateFlow<Indexer.State?>(null)
|
||||
|
||||
/** The current music indexing state. */
|
||||
val indexerState: StateFlow<Indexer.State?> = _indexerState
|
||||
|
||||
private val _libraryExists = MutableStateFlow(false)
|
||||
|
||||
/** Whether a music library has successfully been loaded. */
|
||||
val libraryExists: StateFlow<Boolean> = _libraryExists
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.lang.reflect.Method
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.lazyReflectedMethod
|
||||
|
||||
|
||||
/** A path to a file. [name] is the stripped file name, [parent] is the parent path. */
|
||||
data class Path(val name: String, val parent: Directory)
|
||||
|
||||
|
@ -48,9 +47,9 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
// "primary" actually corresponds to the internal storage, not the primary volume.
|
||||
// Removable storage is represented with the UUID.
|
||||
if (volume.isInternalCompat) {
|
||||
"${DOCUMENT_URI_PRIMARY_NAME}:${relativePath}"
|
||||
"$DOCUMENT_URI_PRIMARY_NAME:$relativePath"
|
||||
} else {
|
||||
volume.uuidCompat?.let { uuid -> "${uuid}:${relativePath}" }
|
||||
volume.uuidCompat?.let { uuid -> "$uuid:$relativePath" }
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.extractor
|
||||
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
||||
/**
|
||||
* TODO: Stub class, not implemented yet
|
||||
*/
|
||||
/** TODO: Stub class, not implemented yet */
|
||||
class CacheLayer {
|
||||
fun init() {
|
||||
// STUB: Add cache database
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.extractor
|
||||
|
||||
import android.content.Context
|
||||
|
@ -9,6 +26,7 @@ import android.provider.MediaStore
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.database.getIntOrNull
|
||||
import androidx.core.database.getStringOrNull
|
||||
import java.io.File
|
||||
import org.oxycblt.auxio.music.Date
|
||||
import org.oxycblt.auxio.music.Directory
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -20,7 +38,6 @@ import org.oxycblt.auxio.settings.Settings
|
|||
import org.oxycblt.auxio.util.contentResolverSafe
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import java.io.File
|
||||
|
||||
/*
|
||||
* This file acts as the base for most the black magic required to get a remotely sensible music
|
||||
|
@ -81,8 +98,8 @@ import java.io.File
|
|||
*/
|
||||
|
||||
/**
|
||||
* The layer that loads music from the MediaStore database. This is an intermediate step in
|
||||
* the music loading process.
|
||||
* The layer that loads music from the MediaStore database. This is an intermediate step in the
|
||||
* music loading process.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
abstract class MediaStoreLayer(private val context: Context, private val cacheLayer: CacheLayer) {
|
||||
|
@ -105,11 +122,10 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
private val settings = Settings(context)
|
||||
|
||||
private val _volumes = mutableListOf<StorageVolume>()
|
||||
protected val volumes: List<StorageVolume> get() = _volumes
|
||||
protected val volumes: List<StorageVolume>
|
||||
get() = _volumes
|
||||
|
||||
/**
|
||||
* Initialize this instance by making a query over the media database.
|
||||
*/
|
||||
/** Initialize this instance by making a query over the media database. */
|
||||
open fun init(): Cursor {
|
||||
cacheLayer.init()
|
||||
|
||||
|
@ -149,7 +165,8 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
|
||||
logD("Starting query [proj: ${projection.toList()}, selector: $selector, args: $args]")
|
||||
|
||||
val cursor = requireNotNull(
|
||||
val cursor =
|
||||
requireNotNull(
|
||||
context.contentResolverSafe.queryCursor(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
projection,
|
||||
|
@ -159,12 +176,12 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
|
||||
idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns._ID)
|
||||
titleIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TITLE)
|
||||
displayNameIndex =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME)
|
||||
displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME)
|
||||
mimeTypeIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.MIME_TYPE)
|
||||
sizeIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.SIZE)
|
||||
dateAddedIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATE_ADDED)
|
||||
dateModifiedIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATE_MODIFIED)
|
||||
dateModifiedIndex =
|
||||
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATE_MODIFIED)
|
||||
durationIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DURATION)
|
||||
yearIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.YEAR)
|
||||
albumIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM)
|
||||
|
@ -175,9 +192,7 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
return cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize this instance by closing the cursor and finalizing the cache.
|
||||
*/
|
||||
/** Finalize this instance by closing the cursor and finalizing the cache. */
|
||||
fun finalize(rawSongs: List<Song.Raw>) {
|
||||
cursor?.close()
|
||||
cursor = null
|
||||
|
@ -281,7 +296,8 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
}
|
||||
|
||||
// The album artist field is nullable and never has placeholder values.
|
||||
raw.albumArtistNames = cursor.getStringOrNull(albumArtistIndex)?.maybeParseSeparators(settings)
|
||||
raw.albumArtistNames =
|
||||
cursor.getStringOrNull(albumArtistIndex)?.maybeParseSeparators(settings)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -303,7 +319,6 @@ abstract class MediaStoreLayer(private val context: Context, private val cacheLa
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Note: The separation between version-specific backends may not be the cleanest. To preserve
|
||||
// speed, we only want to add redundancy on known issues, not with possible issues.
|
||||
|
||||
|
@ -377,7 +392,8 @@ class Api21MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
open class BaseApi29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) : MediaStoreLayer(context, cacheLayer) {
|
||||
open class BaseApi29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
||||
MediaStoreLayer(context, cacheLayer) {
|
||||
private var volumeIndex = -1
|
||||
private var relativePathIndex = -1
|
||||
|
||||
|
@ -431,7 +447,8 @@ open class BaseApi29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
open class Api29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) : BaseApi29MediaStoreLayer(context, cacheLayer) {
|
||||
open class Api29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
||||
BaseApi29MediaStoreLayer(context, cacheLayer) {
|
||||
private var trackIndex = -1
|
||||
|
||||
override fun init(): Cursor {
|
||||
|
@ -462,7 +479,8 @@ open class Api29MediaStoreLayer(context: Context, cacheLayer: CacheLayer) : Base
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
class Api30MediaStoreLayer(context: Context, cacheLayer: CacheLayer) : BaseApi29MediaStoreLayer(context, cacheLayer) {
|
||||
class Api30MediaStoreLayer(context: Context, cacheLayer: CacheLayer) :
|
||||
BaseApi29MediaStoreLayer(context, cacheLayer) {
|
||||
private var trackIndex: Int = -1
|
||||
private var discIndex: Int = -1
|
||||
|
||||
|
|
|
@ -1,20 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.extractor
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.MetadataRetriever
|
||||
import com.google.android.exoplayer2.metadata.Metadata
|
||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
|
||||
import com.google.android.exoplayer2.metadata.vorbis.VorbisComment
|
||||
import org.oxycblt.auxio.music.Date
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.audioUri
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import com.google.android.exoplayer2.metadata.Metadata
|
||||
import org.oxycblt.auxio.music.Date
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
|
||||
/**
|
||||
* The layer that leverages ExoPlayer's metadata retrieval system to index metadata.
|
||||
*
|
||||
|
@ -32,14 +48,10 @@ class MetadataLayer(private val context: Context, private val mediaStoreLayer: M
|
|||
private val settings = Settings(context)
|
||||
private val taskPool: Array<Task?> = arrayOfNulls(TASK_CAPACITY)
|
||||
|
||||
/**
|
||||
* Initialize the sub-layers that this layer relies on.
|
||||
*/
|
||||
/** Initialize the sub-layers that this layer relies on. */
|
||||
fun init() = mediaStoreLayer.init().count
|
||||
|
||||
/**
|
||||
* Finalize the sub-layers that this layer relies on.
|
||||
*/
|
||||
/** Finalize the sub-layers that this layer relies on. */
|
||||
fun finalize(rawSongs: List<Song.Raw>) = mediaStoreLayer.finalize(rawSongs)
|
||||
|
||||
fun parse(emit: (Song.Raw) -> Unit) {
|
||||
|
@ -90,7 +102,6 @@ class MetadataLayer(private val context: Context, private val mediaStoreLayer: M
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
/** The amount of tasks this backend can run efficiently at once. */
|
||||
private const val TASK_CAPACITY = 8
|
||||
|
@ -204,8 +215,7 @@ class Task(context: Context, private val settings: Settings, private val raw: So
|
|||
// 5. ID3v2.3 Release Year, as it is the most common date type
|
||||
(tags["TDOR"]?.run { get(0).parseTimestamp() }
|
||||
?: tags["TDRC"]?.run { get(0).parseTimestamp() }
|
||||
?: tags["TDRL"]?.run { get(0).parseTimestamp() }
|
||||
?: parseId3v23Date(tags))
|
||||
?: tags["TDRL"]?.run { get(0).parseTimestamp() } ?: parseId3v23Date(tags))
|
||||
?.let { raw.date = it }
|
||||
|
||||
// (Sort) Album
|
||||
|
@ -230,7 +240,9 @@ class Task(context: Context, private val settings: Settings, private val raw: So
|
|||
}
|
||||
|
||||
private fun parseId3v23Date(tags: Map<String, List<String>>): Date? {
|
||||
val year = tags["TORY"]?.run { get(0).toIntOrNull() } ?: tags["TYER"]?.run { get(0).toIntOrNull() } ?: return null
|
||||
val year =
|
||||
tags["TORY"]?.run { get(0).toIntOrNull() }
|
||||
?: tags["TYER"]?.run { get(0).toIntOrNull() } ?: return null
|
||||
|
||||
val tdat = tags["TDAT"]
|
||||
return if (tdat != null && tdat[0].length == 4 && tdat[0].isDigitsOnly()) {
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music.extractor
|
||||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
|
@ -50,19 +67,21 @@ fun List<String>.parseMultiValue(settings: Settings) =
|
|||
}
|
||||
|
||||
/**
|
||||
* Maybe a single tag into multi values with the user-preferred separators. If not enabled,
|
||||
* the plain string will be returned.
|
||||
* Maybe a single tag into multi values with the user-preferred separators. If not enabled, the
|
||||
* plain string will be returned.
|
||||
*/
|
||||
fun String.maybeParseSeparators(settings: Settings): List<String> {
|
||||
// Get the separators the user desires. If null, we don't parse any.
|
||||
val separators = settings.separators ?: return listOf(this)
|
||||
|
||||
// Try to cache compiled regexes for particular separator combinations.
|
||||
val regex = synchronized(SEPARATOR_REGEX_CACHE) {
|
||||
val regex =
|
||||
synchronized(SEPARATOR_REGEX_CACHE) {
|
||||
SEPARATOR_REGEX_CACHE.getOrPut(separators) { Regex("[^\\\\][$separators]") }
|
||||
}
|
||||
|
||||
val escape = synchronized(ESCAPE_REGEX_CACHE) {
|
||||
val escape =
|
||||
synchronized(ESCAPE_REGEX_CACHE) {
|
||||
ESCAPE_REGEX_CACHE.getOrPut(separators) { Regex("\\\\[$separators]") }
|
||||
}
|
||||
|
||||
|
@ -72,15 +91,13 @@ fun String.maybeParseSeparators(settings: Settings): List<String> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a multi-value tag into a [ReleaseType], handling separators in the process.
|
||||
*/
|
||||
/** Parse a multi-value tag into a [ReleaseType], handling separators in the process. */
|
||||
fun List<String>.parseReleaseType(settings: Settings) = ReleaseType.parse(parseMultiValue(settings))
|
||||
|
||||
/**
|
||||
* Parse a multi-value genre name using ID3v2 rules. If there is one value, the ID3v2.3
|
||||
* rules will be used, followed by separator parsing. Otherwise, each value will be iterated
|
||||
* through, and numeric values transformed into string values.
|
||||
* Parse a multi-value genre name using ID3v2 rules. If there is one value, the ID3v2.3 rules will
|
||||
* be used, followed by separator parsing. Otherwise, each value will be iterated through, and
|
||||
* numeric values transformed into string values.
|
||||
*/
|
||||
fun List<String>.parseId3GenreNames(settings: Settings) =
|
||||
if (size == 1) {
|
||||
|
@ -89,13 +106,9 @@ fun List<String>.parseId3GenreNames(settings: Settings) =
|
|||
map { it.parseId3v1Genre() ?: it }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single genre name using ID3v2.3 rules.
|
||||
*/
|
||||
/** Parse a single genre name using ID3v2.3 rules. */
|
||||
fun String.parseId3GenreNames(settings: Settings) =
|
||||
parseId3v1Genre()?.let { listOf(it) } ?:
|
||||
parseId3v2Genre() ?:
|
||||
maybeParseSeparators(settings)
|
||||
parseId3v1Genre()?.let { listOf(it) } ?: parseId3v2Genre() ?: maybeParseSeparators(settings)
|
||||
|
||||
private fun String.parseId3v1Genre(): String? =
|
||||
when {
|
||||
|
|
|
@ -26,7 +26,11 @@ import kotlinx.coroutines.CancellationException
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.music.*
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.extractor.Api21MediaStoreLayer
|
||||
import org.oxycblt.auxio.music.extractor.Api29MediaStoreLayer
|
||||
import org.oxycblt.auxio.music.extractor.Api30MediaStoreLayer
|
||||
|
@ -50,8 +54,8 @@ import org.oxycblt.auxio.util.logW
|
|||
* 3. Using the songs to build the library, which primarily involves linking up all data objects
|
||||
* with their corresponding parents/children.
|
||||
*
|
||||
* This class in particular handles 3 primarily. For the code that handles 1 and 2, see the
|
||||
* layer implementations.
|
||||
* This class in particular handles 3 primarily. For the code that handles 1 and 2, see the layer
|
||||
* implementations.
|
||||
*
|
||||
* This class also fulfills the role of maintaining the current music loading state, which seems
|
||||
* like a job for [MusicStore] but in practice is only really leveraged by the components that
|
||||
|
@ -205,8 +209,10 @@ class Indexer {
|
|||
|
||||
val mediaStoreLayer =
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Api30MediaStoreLayer(context, cacheLayer)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Api29MediaStoreLayer(context, cacheLayer)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->
|
||||
Api30MediaStoreLayer(context, cacheLayer)
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ->
|
||||
Api29MediaStoreLayer(context, cacheLayer)
|
||||
else -> Api21MediaStoreLayer(context, cacheLayer)
|
||||
}
|
||||
|
||||
|
@ -234,8 +240,8 @@ class Indexer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Does the initial query over the song database using [metadataLayer]. The songs returned by this
|
||||
* function are **not** well-formed. The companion [buildAlbums], [buildArtists], and
|
||||
* Does the initial query over the song database using [metadataLayer]. The songs returned by
|
||||
* this function are **not** well-formed. The companion [buildAlbums], [buildArtists], and
|
||||
* [buildGenres] functions must be called with the returned list so that all songs are properly
|
||||
* linked up.
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,10 @@ package org.oxycblt.auxio.music.system
|
|||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.database.ContentObserver
|
||||
import android.os.*
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.provider.MediaStore
|
||||
import coil.imageLoader
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
|
@ -41,7 +41,7 @@ constructor(
|
|||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.oxycblt.auxio.IntegerTable
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
|
@ -33,7 +34,6 @@ import org.oxycblt.auxio.util.androidActivityViewModels
|
|||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getColorCompat
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
|
||||
/**
|
||||
* A fragment showing the current playback state in a compact manner. Used as the bar for the
|
||||
|
|
|
@ -26,10 +26,13 @@ import org.oxycblt.auxio.IntegerTable
|
|||
enum class PlaybackMode {
|
||||
/** Construct the queue from the genre's songs */
|
||||
ALL_SONGS,
|
||||
|
||||
/** Construct the queue from the artist's songs */
|
||||
IN_ALBUM,
|
||||
|
||||
/** Construct the queue from the album's songs */
|
||||
IN_ARTIST,
|
||||
|
||||
/** Construct the queue from all songs */
|
||||
IN_GENRE;
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
||||
import org.oxycblt.auxio.ui.fragment.MenuFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
||||
|
|
|
@ -30,15 +30,15 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.dsToMs
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
import org.oxycblt.auxio.playback.state.InternalPlayer
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateDatabase
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.application
|
||||
import org.oxycblt.auxio.music.dsToMs
|
||||
import org.oxycblt.auxio.util.logE
|
||||
import org.oxycblt.auxio.music.msToDs
|
||||
|
||||
/**
|
||||
* The ViewModel that provides a UI frontend for [PlaybackStateManager].
|
||||
|
@ -54,21 +54,25 @@ class PlaybackViewModel(application: Application) :
|
|||
private val playbackManager = PlaybackStateManager.getInstance()
|
||||
|
||||
private val _song = MutableStateFlow<Song?>(null)
|
||||
|
||||
/** The current song. */
|
||||
val song: StateFlow<Song?>
|
||||
get() = _song
|
||||
private val _parent = MutableStateFlow<MusicParent?>(null)
|
||||
|
||||
/** The current model that is being played from, such as an [Album] or [Artist] */
|
||||
val parent: StateFlow<MusicParent?> = _parent
|
||||
private val _isPlaying = MutableStateFlow(false)
|
||||
val isPlaying: StateFlow<Boolean>
|
||||
get() = _isPlaying
|
||||
private val _positionDs = MutableStateFlow(0L)
|
||||
|
||||
/** The current playback position, in *deci-seconds* */
|
||||
val positionDs: StateFlow<Long>
|
||||
get() = _positionDs
|
||||
|
||||
private val _repeatMode = MutableStateFlow(RepeatMode.NONE)
|
||||
|
||||
/** The current repeat mode, see [RepeatMode] for more information */
|
||||
val repeatMode: StateFlow<RepeatMode>
|
||||
get() = _repeatMode
|
||||
|
|
|
@ -43,11 +43,7 @@ import org.oxycblt.auxio.util.logD
|
|||
*/
|
||||
class StyledSeekBar
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) :
|
||||
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
|
||||
ForcedLTRFrameLayout(context, attrs, defStyleAttr),
|
||||
Slider.OnSliderTouchListener,
|
||||
Slider.OnChangeListener {
|
||||
|
|
|
@ -28,8 +28,13 @@ import com.google.android.material.shape.MaterialShapeDrawable
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.recycler.*
|
||||
import org.oxycblt.auxio.util.*
|
||||
import org.oxycblt.auxio.ui.recycler.IndicatorAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.SyncListDiffer
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
||||
class QueueAdapter(private val listener: QueueItemListener) :
|
||||
RecyclerView.Adapter<QueueSongViewHolder>() {
|
||||
|
@ -104,10 +109,8 @@ interface QueueItemListener {
|
|||
fun onPickUp(viewHolder: RecyclerView.ViewHolder)
|
||||
}
|
||||
|
||||
class QueueSongViewHolder
|
||||
private constructor(
|
||||
private val binding: ItemQueueSongBinding,
|
||||
) : IndicatorAdapter.ViewHolder(binding.root) {
|
||||
class QueueSongViewHolder private constructor(private val binding: ItemQueueSongBinding) :
|
||||
IndicatorAdapter.ViewHolder(binding.root) {
|
||||
val bodyView: View
|
||||
get() = binding.body
|
||||
val backgroundView: View
|
||||
|
|
|
@ -25,7 +25,11 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.AuxioSheetBehavior
|
||||
import org.oxycblt.auxio.util.*
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.getDimenSize
|
||||
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
||||
/**
|
||||
* The bottom sheet behavior designed for the queue in particular.
|
||||
|
|
|
@ -23,8 +23,10 @@ import org.oxycblt.auxio.IntegerTable
|
|||
enum class ReplayGainMode {
|
||||
/** Apply the track gain, falling back to the album gain if the track gain is not found. */
|
||||
TRACK,
|
||||
|
||||
/** Apply the album gain, falling back to the track gain if the album gain is not found. */
|
||||
ALBUM,
|
||||
|
||||
/** Apply the album gain only when playing from an album, defaulting to track gain otherwise. */
|
||||
DYNAMIC;
|
||||
|
||||
|
@ -46,5 +48,5 @@ data class ReplayGainPreAmp(
|
|||
/** The value to use when ReplayGain tags are present. */
|
||||
val with: Float,
|
||||
/** The value to use when ReplayGain tags are not present. */
|
||||
val without: Float,
|
||||
val without: Float
|
||||
)
|
||||
|
|
|
@ -116,8 +116,7 @@ class PlaybackStateDatabase private constructor(context: Context) :
|
|||
queue = queue,
|
||||
positionMs = rawState.positionMs,
|
||||
repeatMode = rawState.repeatMode,
|
||||
isShuffled = rawState.isShuffled,
|
||||
)
|
||||
isShuffled = rawState.isShuffled)
|
||||
}
|
||||
|
||||
private fun readRawState(): RawState? {
|
||||
|
@ -258,7 +257,7 @@ class PlaybackStateDatabase private constructor(context: Context) :
|
|||
val parent: MusicParent?,
|
||||
val positionMs: Long,
|
||||
val repeatMode: RepeatMode,
|
||||
val isShuffled: Boolean,
|
||||
val isShuffled: Boolean
|
||||
)
|
||||
|
||||
private data class RawState(
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager.Callback
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
@ -58,13 +59,16 @@ class PlaybackStateManager private constructor() {
|
|||
/** The currently playing song. Null if there isn't one */
|
||||
val song
|
||||
get() = queue.getOrNull(index)
|
||||
|
||||
/** The parent the queue is based on, null if all songs */
|
||||
var parent: MusicParent? = null
|
||||
private set
|
||||
private var _queue = mutableListOf<Song>()
|
||||
|
||||
/** The current queue determined by [parent] */
|
||||
val queue
|
||||
get() = _queue
|
||||
|
||||
/** The current position in the queue */
|
||||
var index = -1
|
||||
private set
|
||||
|
@ -79,6 +83,7 @@ class PlaybackStateManager private constructor() {
|
|||
field = value
|
||||
notifyRepeatModeChanged()
|
||||
}
|
||||
|
||||
/** Whether the queue is shuffled */
|
||||
var isShuffled = false
|
||||
private set
|
||||
|
|
|
@ -53,6 +53,7 @@ class SearchViewModel(application: Application) :
|
|||
private val settings = Settings(application)
|
||||
|
||||
private val _searchResults = MutableStateFlow(listOf<Item>())
|
||||
|
||||
/** Current search results from the last [search] call. */
|
||||
val searchResults: StateFlow<List<Item>>
|
||||
get() = _searchResults
|
||||
|
|
|
@ -36,10 +36,10 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.music.formatDurationMs
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
|
|
@ -19,7 +19,9 @@ package org.oxycblt.auxio.settings
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.storage.StorageManager
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
|
@ -34,7 +36,6 @@ import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp
|
|||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.accent.Accent
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
|
@ -215,13 +216,12 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of separators the user wants to parse by.
|
||||
*/
|
||||
/** The list of separators the user wants to parse by. */
|
||||
var separators: String?
|
||||
// Differ from convention and store a string of separator characters instead of an int
|
||||
// code. This makes it easier to use in Regexes and makes it more extendable.
|
||||
get() = inner.getString(context.getString(R.string.set_key_separators), null)?.ifEmpty { null }
|
||||
get() =
|
||||
inner.getString(context.getString(R.string.set_key_separators), null)?.ifEmpty { null }
|
||||
set(value) {
|
||||
inner.edit {
|
||||
putString(context.getString(R.string.set_key_separators), value)
|
||||
|
@ -344,3 +344,34 @@ class Settings(private val context: Context, private val callback: Callback? = n
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- COMPAT ---
|
||||
|
||||
fun handleAccentCompat(context: Context, prefs: SharedPreferences): Accent {
|
||||
val currentKey = context.getString(R.string.set_key_accent)
|
||||
|
||||
if (prefs.contains(OldKeys.KEY_ACCENT3)) {
|
||||
Log.d("Auxio.SettingsCompat", "Migrating ${OldKeys.KEY_ACCENT3}")
|
||||
|
||||
var accent = prefs.getInt(OldKeys.KEY_ACCENT3, 5)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Accents were previously frozen as soon as the OS was updated to android twelve,
|
||||
// as dynamic colors were enabled by default. This is no longer the case, so we need
|
||||
// to re-update the setting to dynamic colors here.
|
||||
accent = 16
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(currentKey, accent)
|
||||
remove(OldKeys.KEY_ACCENT3)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
return Accent.from(prefs.getInt(currentKey, Accent.DEFAULT))
|
||||
}
|
||||
|
||||
/** Cache of the old keys used in Auxio. */
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT3 = "auxio_accent"
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.content.edit
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.accent.Accent
|
||||
|
||||
// A couple of utils for migrating from old settings values to the new formats.
|
||||
// Usually, these will last for 6 months before being removed.
|
||||
|
||||
fun handleAccentCompat(context: Context, prefs: SharedPreferences): Accent {
|
||||
val currentKey = context.getString(R.string.set_key_accent)
|
||||
|
||||
if (prefs.contains(OldKeys.KEY_ACCENT3)) {
|
||||
Log.d("Auxio.SettingsCompat", "Migrating ${OldKeys.KEY_ACCENT3}")
|
||||
|
||||
var accent = prefs.getInt(OldKeys.KEY_ACCENT3, 5)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Accents were previously frozen as soon as the OS was updated to android twelve,
|
||||
// as dynamic colors were enabled by default. This is no longer the case, so we need
|
||||
// to re-update the setting to dynamic colors here.
|
||||
accent = 16
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(currentKey, accent)
|
||||
remove(OldKeys.KEY_ACCENT3)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
return Accent.from(prefs.getInt(currentKey, Accent.DEFAULT))
|
||||
}
|
||||
|
||||
/** Cache of the old keys used in Auxio. */
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT3 = "auxio_accent"
|
||||
}
|
|
@ -26,7 +26,8 @@ import android.view.WindowInsets
|
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.bottomsheet.NeoBottomSheetBehavior
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.util.*
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.systemGestureInsetsCompat
|
||||
|
||||
/**
|
||||
* Implements a reasonable enough skeleton around BottomSheetBehavior (Excluding auxio extensions in
|
||||
|
|
|
@ -30,11 +30,13 @@ import org.oxycblt.auxio.util.logD
|
|||
*/
|
||||
class NavigationViewModel : ViewModel() {
|
||||
private val _mainNavigationAction = MutableStateFlow<MainNavigationAction?>(null)
|
||||
|
||||
/** Flag for main fragment navigation. Intended for MainFragment use only. */
|
||||
val mainNavigationAction: StateFlow<MainNavigationAction?>
|
||||
get() = _mainNavigationAction
|
||||
|
||||
private val _exploreNavigationItem = MutableStateFlow<Music?>(null)
|
||||
|
||||
/**
|
||||
* Flag for navigation within the explore fragments. Observe this to coordinate navigation to an
|
||||
* item's UI.
|
||||
|
@ -85,12 +87,16 @@ class NavigationViewModel : ViewModel() {
|
|||
sealed class MainNavigationAction {
|
||||
/** Expand the playback panel. */
|
||||
object Expand : MainNavigationAction()
|
||||
|
||||
/** Collapse the playback panel. */
|
||||
object Collapse : MainNavigationAction()
|
||||
|
||||
/** Go to settings. */
|
||||
object Settings : MainNavigationAction()
|
||||
|
||||
/** Go to the about page. */
|
||||
object About : MainNavigationAction()
|
||||
|
||||
/** Show song details. */
|
||||
data class SongDetails(val song: Song) : MainNavigationAction()
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.oxycblt.auxio.ui
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
import kotlin.UnsupportedOperationException
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
|
@ -27,6 +26,7 @@ import org.oxycblt.auxio.music.Date
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.Sort.Mode
|
||||
|
||||
/**
|
||||
* Represents the sort modes used in Auxio.
|
||||
|
|
|
@ -349,7 +349,6 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
if (!dragging &&
|
||||
thumbView.isUnder(downX, thumbView.top.toFloat(), minTouchTargetSize) &&
|
||||
abs(eventY - downY) > touchSlop) {
|
||||
|
||||
if (thumbView.isUnder(downX, downY, minTouchTargetSize)) {
|
||||
dragStartY = lastY
|
||||
dragStartThumbOffset = thumbOffset
|
||||
|
|
|
@ -73,10 +73,8 @@ class SongViewHolder private constructor(private val binding: ItemSongBinding) :
|
|||
* The Shared ViewHolder for a [Album].
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class AlbumViewHolder
|
||||
private constructor(
|
||||
private val binding: ItemParentBinding,
|
||||
) : IndicatorAdapter.ViewHolder(binding.root) {
|
||||
class AlbumViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
IndicatorAdapter.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: Album, listener: MenuItemListener) {
|
||||
binding.parentImage.bind(item)
|
||||
|
@ -157,10 +155,8 @@ class ArtistViewHolder private constructor(private val binding: ItemParentBindin
|
|||
* The Shared ViewHolder for a [Genre].
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class GenreViewHolder
|
||||
private constructor(
|
||||
private val binding: ItemParentBinding,
|
||||
) : IndicatorAdapter.ViewHolder(binding.root) {
|
||||
class GenreViewHolder private constructor(private val binding: ItemParentBinding) :
|
||||
IndicatorAdapter.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: Genre, listener: MenuItemListener) {
|
||||
binding.parentImage.bind(item)
|
||||
|
|
|
@ -48,7 +48,6 @@ fun Int.nonZeroOrNull() = if (this > 0) this else null
|
|||
/** Returns null if this value is not in [range]. */
|
||||
fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else null
|
||||
|
||||
|
||||
/** Lazily reflect to retrieve a [Field]. */
|
||||
fun lazyReflectedField(clazz: KClass<*>, field: String) = lazy {
|
||||
clazz.java.getDeclaredField(field).also { it.isAccessible = true }
|
||||
|
|
|
@ -44,6 +44,7 @@ fun createThinWidget(context: Context, state: WidgetComponent.WidgetState) =
|
|||
.applyRoundingToBackground(context)
|
||||
.applyMeta(context, state)
|
||||
.applyBasicControls(context, state)
|
||||
|
||||
/**
|
||||
* The small widget is for 2x2 widgets and just shows the cover art and playback controls. This is
|
||||
* generally because a Medium widget is too large for this widget size and a text-only widget is too
|
||||
|
|
|
@ -160,6 +160,6 @@ class WidgetComponent(private val context: Context) :
|
|||
val cover: Bitmap?,
|
||||
val isPlaying: Boolean,
|
||||
val repeatMode: RepeatMode,
|
||||
val isShuffled: Boolean,
|
||||
val isShuffled: Boolean
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ buildscript {
|
|||
classpath 'com.android.tools.build:gradle:7.4.0-alpha10'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
||||
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.6.1"
|
||||
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.10.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
6
gradlew
vendored
6
gradlew
vendored
|
@ -205,6 +205,12 @@ set -- \
|
|||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
|
10
gradlew.bat
vendored
10
gradlew.bat
vendored
|
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
Loading…
Reference in a new issue