build: update deps
I didn't fully keep track of what I did this time, mostly since I was busy wrangling some of the insane build issues from this gradle update.
This commit is contained in:
parent
4b49174ced
commit
68a9ce7b09
45 changed files with 186 additions and 39 deletions
|
@ -6,7 +6,8 @@ plugins {
|
|||
id "kotlin-parcelize"
|
||||
id "dagger.hilt.android.plugin"
|
||||
id "kotlin-kapt"
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id "com.google.devtools.ksp"
|
||||
id "org.jetbrains.kotlin.android"
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -118,7 +119,9 @@ dependencies {
|
|||
// Database
|
||||
def room_version = '2.6.0-alpha02'
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
// I have no clue why, but using KSP breaks the playlist database definition.
|
||||
//noinspection KaptUsageInsteadOfKsp
|
||||
ksp "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// --- THIRD PARTY ---
|
||||
|
|
|
@ -102,6 +102,7 @@ constructor(
|
|||
/** The current list data derived from [currentAlbum]. */
|
||||
val albumList: StateFlow<List<Item>>
|
||||
get() = _albumList
|
||||
|
||||
private val _albumInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for updating [albumList] in the UI. */
|
||||
val albumInstructions: Event<UpdateInstructions>
|
||||
|
|
|
@ -106,6 +106,7 @@ sealed interface ArtistShowChoices {
|
|||
class FromSong(val song: Song) : ArtistShowChoices {
|
||||
override val uid = song.uid
|
||||
override val choices = song.artists
|
||||
|
||||
override fun sanitize(newLibrary: DeviceLibrary) =
|
||||
newLibrary.findSong(uid)?.let { FromSong(it) }
|
||||
}
|
||||
|
@ -116,6 +117,7 @@ sealed interface ArtistShowChoices {
|
|||
data class FromAlbum(val album: Album) : ArtistShowChoices {
|
||||
override val uid = album.uid
|
||||
override val choices = album.artists
|
||||
|
||||
override fun sanitize(newLibrary: DeviceLibrary) =
|
||||
newLibrary.findAlbum(uid)?.let { FromAlbum(it) }
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ class ArtistDetailHeaderAdapter(private val listener: Listener) :
|
|||
DetailHeaderAdapter<Artist, ArtistDetailHeaderViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
ArtistDetailHeaderViewHolder.from(parent)
|
||||
|
||||
override fun onBindHeader(holder: ArtistDetailHeaderViewHolder, parent: Artist) =
|
||||
holder.bind(parent, listener)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,9 @@ import org.oxycblt.auxio.util.logD
|
|||
abstract class DetailHeaderAdapter<T : MusicParent, VH : RecyclerView.ViewHolder> :
|
||||
RecyclerView.Adapter<VH>() {
|
||||
private var currentParent: T? = null
|
||||
|
||||
final override fun getItemCount() = 1
|
||||
|
||||
final override fun onBindViewHolder(holder: VH, position: Int) =
|
||||
onBindHeader(holder, requireNotNull(currentParent))
|
||||
|
||||
|
|
|
@ -211,6 +211,7 @@ private constructor(private val binding: ItemEditableSongBinding) :
|
|||
PlaylistDetailListAdapter.ViewHolder {
|
||||
override val enabled: Boolean
|
||||
get() = binding.songDragHandle.isVisible
|
||||
|
||||
override val root = binding.root
|
||||
override val body = binding.body
|
||||
override val delete = binding.background
|
||||
|
|
|
@ -622,6 +622,7 @@ class HomeFragment :
|
|||
lifecycleOwner: LifecycleOwner
|
||||
) : FragmentStateAdapter(fragmentManager, lifecycleOwner.lifecycle) {
|
||||
override fun getItemCount() = tabs.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment =
|
||||
when (tabs[position]) {
|
||||
MusicType.SONGS -> SongListFragment()
|
||||
|
|
|
@ -59,6 +59,7 @@ constructor(
|
|||
/** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
val songsList: StateFlow<List<Song>>
|
||||
get() = _songsList
|
||||
|
||||
private val _songsInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for how to update [songsList] in the UI. */
|
||||
val songsInstructions: Event<UpdateInstructions>
|
||||
|
@ -68,6 +69,7 @@ constructor(
|
|||
/** A list of [Album]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
val albumsList: StateFlow<List<Album>>
|
||||
get() = _albumsLists
|
||||
|
||||
private val _albumsInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for how to update [albumsList] in the UI. */
|
||||
val albumsInstructions: Event<UpdateInstructions>
|
||||
|
@ -80,6 +82,7 @@ constructor(
|
|||
*/
|
||||
val artistsList: MutableStateFlow<List<Artist>>
|
||||
get() = _artistsList
|
||||
|
||||
private val _artistsInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for how to update [artistsList] in the UI. */
|
||||
val artistsInstructions: Event<UpdateInstructions>
|
||||
|
@ -89,6 +92,7 @@ constructor(
|
|||
/** A list of [Genre]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
val genresList: StateFlow<List<Genre>>
|
||||
get() = _genresList
|
||||
|
||||
private val _genresInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for how to update [genresList] in the UI. */
|
||||
val genresInstructions: Event<UpdateInstructions>
|
||||
|
@ -98,6 +102,7 @@ constructor(
|
|||
/** A list of [Playlist]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
val playlistsList: StateFlow<List<Playlist>>
|
||||
get() = _playlistsList
|
||||
|
||||
private val _playlistsInstructions = MutableEvent<UpdateInstructions>()
|
||||
/** Instructions for how to update [genresList] in the UI. */
|
||||
val playlistsInstructions: Event<UpdateInstructions>
|
||||
|
@ -289,5 +294,6 @@ constructor(
|
|||
|
||||
sealed interface Outer {
|
||||
object Settings : Outer
|
||||
|
||||
object About : Outer
|
||||
}
|
||||
|
|
|
@ -123,8 +123,11 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0)
|
|||
}
|
||||
|
||||
override fun isAutoMirrored(): Boolean = true
|
||||
|
||||
override fun setAlpha(alpha: Int) {}
|
||||
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {}
|
||||
|
||||
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
|
||||
|
||||
private fun updatePath() {
|
||||
|
|
|
@ -42,7 +42,9 @@ class TabAdapter(private val listener: EditClickListListener<Tab>) :
|
|||
private set
|
||||
|
||||
override fun getItemCount() = tabs.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = TabViewHolder.from(parent)
|
||||
|
||||
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
|
||||
holder.bind(tabs[position], listener)
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
val playingDrawable: AnimationDrawable,
|
||||
val pausedDrawable: Drawable
|
||||
)
|
||||
|
||||
private val playbackIndicator: PlaybackIndicator?
|
||||
private val selectionBadge: ImageView?
|
||||
|
||||
|
@ -105,6 +106,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
val desc: String,
|
||||
@DrawableRes val errorRes: Int
|
||||
)
|
||||
|
||||
private var currentCover: Cover? = null
|
||||
|
||||
init {
|
||||
|
|
|
@ -225,6 +225,7 @@ sealed interface Menu {
|
|||
@get:MenuRes val res: Int
|
||||
/** A [Parcel] version of this instance that can be used as a navigation argument. */
|
||||
val parcel: Parcel
|
||||
|
||||
sealed interface Parcel : Parcelable
|
||||
|
||||
/** Navigate to a [Song] menu dialog. */
|
||||
|
@ -257,6 +258,7 @@ sealed interface Menu {
|
|||
class ForAlbum(@MenuRes override val res: Int, val album: Album) : Menu {
|
||||
override val parcel
|
||||
get() = Parcel(res, album.uid)
|
||||
|
||||
@Parcelize data class Parcel(val res: Int, val albumUid: Music.UID) : Menu.Parcel
|
||||
}
|
||||
|
||||
|
@ -264,6 +266,7 @@ sealed interface Menu {
|
|||
class ForArtist(@MenuRes override val res: Int, val artist: Artist) : Menu {
|
||||
override val parcel
|
||||
get() = Parcel(res, artist.uid)
|
||||
|
||||
@Parcelize data class Parcel(val res: Int, val artistUid: Music.UID) : Menu.Parcel
|
||||
}
|
||||
|
||||
|
@ -271,6 +274,7 @@ sealed interface Menu {
|
|||
class ForGenre(@MenuRes override val res: Int, val genre: Genre) : Menu {
|
||||
override val parcel
|
||||
get() = Parcel(res, genre.uid)
|
||||
|
||||
@Parcelize data class Parcel(val res: Int, val genreUid: Music.UID) : Menu.Parcel
|
||||
}
|
||||
|
||||
|
@ -278,6 +282,7 @@ sealed interface Menu {
|
|||
class ForPlaylist(@MenuRes override val res: Int, val playlist: Playlist) : Menu {
|
||||
override val parcel
|
||||
get() = Parcel(res, playlist.uid)
|
||||
|
||||
@Parcelize data class Parcel(val res: Int, val playlistUid: Music.UID) : Menu.Parcel
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ abstract class FlexibleListAdapter<T, VH : RecyclerView.ViewHolder>(
|
|||
diffCallback: DiffUtil.ItemCallback<T>
|
||||
) : RecyclerView.Adapter<VH>() {
|
||||
@Suppress("LeakingThis") private val differ = FlexibleListDiffer(this, diffCallback)
|
||||
|
||||
final override fun getItemCount() = differ.currentList.size
|
||||
/** The current list stored by the adapter's differ instance. */
|
||||
val currentList: List<T>
|
||||
|
@ -118,6 +119,7 @@ private class FlexibleListDiffer<T>(
|
|||
|
||||
private class MainThreadExecutor : Executor {
|
||||
val mHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun execute(command: Runnable) {
|
||||
mHandler.post(command)
|
||||
}
|
||||
|
|
|
@ -28,5 +28,6 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
interface MusicModule {
|
||||
@Singleton @Binds fun repository(musicRepository: MusicRepositoryImpl): MusicRepository
|
||||
|
||||
@Binds fun settings(musicSettingsImpl: MusicSettingsImpl): MusicSettings
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ class MusicSettingsImpl @Inject constructor(@ApplicationContext context: Context
|
|||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override var albumSongSort: Sort
|
||||
get() {
|
||||
var sort =
|
||||
|
|
|
@ -40,7 +40,9 @@ abstract class CacheDatabase : RoomDatabase() {
|
|||
@Dao
|
||||
interface CachedSongsDao {
|
||||
@Query("SELECT * FROM CachedSong") suspend fun readSongs(): List<CachedSong>
|
||||
|
||||
@Query("DELETE FROM CachedSong") suspend fun nukeSongs()
|
||||
|
||||
@Insert suspend fun insertSongs(songs: List<CachedSong>)
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ private class CacheImpl(cachedSongs: List<CachedSong>) : Cache {
|
|||
}
|
||||
|
||||
override var invalidated = false
|
||||
|
||||
override fun populate(rawSong: RawSong): Boolean {
|
||||
// For a cached raw song to be used, it must exist within the cache and have matching
|
||||
// addition and modification timestamps. Technically the addition timestamp doesn't
|
||||
|
|
|
@ -266,14 +266,19 @@ class DeviceLibraryImpl(
|
|||
|
||||
// All other music is built from songs, so comparison only needs to check songs.
|
||||
override fun equals(other: Any?) = other is DeviceLibrary && other.songs == songs
|
||||
|
||||
override fun hashCode() = songs.hashCode()
|
||||
|
||||
override fun toString() =
|
||||
"DeviceLibrary(songs=${songs.size}, albums=${albums.size}, " +
|
||||
"artists=${artists.size}, genres=${genres.size})"
|
||||
|
||||
override fun findSong(uid: Music.UID): Song? = songUidMap[uid]
|
||||
|
||||
override fun findAlbum(uid: Music.UID): Album? = albumUidMap[uid]
|
||||
|
||||
override fun findArtist(uid: Music.UID): Artist? = artistUidMap[uid]
|
||||
|
||||
override fun findGenre(uid: Music.UID): Genre? = genreUidMap[uid]
|
||||
|
||||
override fun findSongForUri(context: Context, uri: Uri) =
|
||||
|
|
|
@ -102,8 +102,10 @@ class SongImpl(private val rawSong: RawSong, musicSettings: MusicSettings) : Son
|
|||
private val hashCode = 31 * uid.hashCode() + rawSong.hashCode()
|
||||
|
||||
override fun hashCode() = hashCode
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is SongImpl && uid == other.uid && rawSong == other.rawSong
|
||||
|
||||
override fun toString() = "Song(uid=$uid, name=$name)"
|
||||
|
||||
private val artistMusicBrainzIds = rawSong.artistMusicBrainzIds.parseMultiValue(musicSettings)
|
||||
|
@ -313,8 +315,10 @@ class AlbumImpl(
|
|||
}
|
||||
|
||||
override fun hashCode() = hashCode
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is AlbumImpl && uid == other.uid && rawAlbum == other.rawAlbum && songs == other.songs
|
||||
|
||||
override fun toString() = "Album(uid=$uid, name=$name)"
|
||||
|
||||
/**
|
||||
|
|
|
@ -74,9 +74,13 @@ interface MediaStoreExtractor {
|
|||
/** A black-box interface representing a query from the media database. */
|
||||
interface Query {
|
||||
val projectedTotal: Int
|
||||
|
||||
fun moveToNext(): Boolean
|
||||
|
||||
fun close()
|
||||
|
||||
fun populateFileInfo(rawSong: RawSong)
|
||||
|
||||
fun populateTags(rawSong: RawSong)
|
||||
}
|
||||
|
||||
|
@ -285,7 +289,9 @@ private abstract class BaseMediaStoreExtractor(
|
|||
private val albumArtistIndex = cursor.getColumnIndexOrThrow(AUDIO_COLUMN_ALBUM_ARTIST)
|
||||
|
||||
final override val projectedTotal = cursor.count
|
||||
|
||||
final override fun moveToNext() = cursor.moveToNext()
|
||||
|
||||
final override fun close() = cursor.close()
|
||||
|
||||
override fun populateFileInfo(rawSong: RawSong) {
|
||||
|
@ -524,6 +530,7 @@ private class Api29MediaStoreExtractor(context: Context, musicSettings: MusicSet
|
|||
storageManager: StorageManager
|
||||
) : BaseApi29MediaStoreExtractor.Query(cursor, genreNamesMap, storageManager) {
|
||||
private val trackIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TRACK)
|
||||
|
||||
override fun populateTags(rawSong: RawSong) {
|
||||
super.populateTags(rawSong)
|
||||
// This extractor is volume-aware, but does not support the modern track columns.
|
||||
|
|
|
@ -74,8 +74,11 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
|||
}
|
||||
|
||||
override fun equals(other: Any?) = other is Date && compareTo(other) == 0
|
||||
|
||||
override fun hashCode() = tokens.hashCode()
|
||||
|
||||
override fun toString() = StringBuilder().appendDate().toString()
|
||||
|
||||
override fun compareTo(other: Date): Int {
|
||||
for (i in 0 until max(tokens.size, other.tokens.size)) {
|
||||
val ai = tokens.getOrNull(i)
|
||||
|
|
|
@ -29,6 +29,8 @@ import org.oxycblt.auxio.list.Item
|
|||
class Disc(val number: Int, val name: String?) : Item, Comparable<Disc> {
|
||||
// We don't want to group discs by differing subtitles, so only compare by the number
|
||||
override fun equals(other: Any?) = other is Disc && number == other.number
|
||||
|
||||
override fun hashCode() = number.hashCode()
|
||||
|
||||
override fun compareTo(other: Disc) = number.compareTo(other.number)
|
||||
}
|
||||
|
|
|
@ -132,7 +132,9 @@ sealed interface Name : Comparable<Name> {
|
|||
*/
|
||||
data class Unknown(@StringRes val stringRes: Int) : Name {
|
||||
override val thumb = "?"
|
||||
|
||||
override fun resolve(context: Context) = context.getString(stringRes)
|
||||
|
||||
override fun compareTo(other: Name) =
|
||||
when (other) {
|
||||
// Unknown names do not need any direct comparison right now.
|
||||
|
|
|
@ -27,6 +27,8 @@ import dagger.hilt.components.SingletonComponent
|
|||
@InstallIn(SingletonComponent::class)
|
||||
interface MetadataModule {
|
||||
@Binds fun tagExtractor(extractor: TagExtractorImpl): TagExtractor
|
||||
|
||||
@Binds fun tagWorkerFactory(factory: TagWorkerFactoryImpl): TagWorker.Factory
|
||||
|
||||
@Binds fun audioPropertiesFactory(factory: AudioPropertiesFactoryImpl): AudioProperties.Factory
|
||||
}
|
||||
|
|
|
@ -42,7 +42,9 @@ private constructor(
|
|||
|
||||
override fun equals(other: Any?) =
|
||||
other is PlaylistImpl && uid == other.uid && name == other.name && songs == other.songs
|
||||
|
||||
override fun hashCode() = hashCode
|
||||
|
||||
override fun toString() = "Playlist(uid=$uid, name=$name)"
|
||||
|
||||
/**
|
||||
|
|
|
@ -171,7 +171,9 @@ private class UserLibraryImpl(
|
|||
private val musicSettings: MusicSettings
|
||||
) : MutableUserLibrary {
|
||||
override fun hashCode() = playlistMap.hashCode()
|
||||
|
||||
override fun equals(other: Any?) = other is UserLibraryImpl && other.playlistMap == playlistMap
|
||||
|
||||
override fun toString() = "UserLibrary(playlists=${playlists.size})"
|
||||
|
||||
override val playlists: Collection<Playlist>
|
||||
|
|
|
@ -51,7 +51,7 @@ abstract class UserMusicDatabase : RoomDatabase() {
|
|||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@Dao
|
||||
interface PlaylistDao {
|
||||
abstract class PlaylistDao() {
|
||||
/**
|
||||
* Read out all playlists stored in the database.
|
||||
*
|
||||
|
@ -59,7 +59,7 @@ interface PlaylistDao {
|
|||
*/
|
||||
@Transaction
|
||||
@Query("SELECT * FROM PlaylistInfo")
|
||||
suspend fun readRawPlaylists(): List<RawPlaylist>
|
||||
abstract suspend fun readRawPlaylists(): List<RawPlaylist>
|
||||
|
||||
/**
|
||||
* Create a new playlist.
|
||||
|
@ -67,7 +67,7 @@ interface PlaylistDao {
|
|||
* @param rawPlaylist The [RawPlaylist] to create.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun insertPlaylist(rawPlaylist: RawPlaylist) {
|
||||
open suspend fun insertPlaylist(rawPlaylist: RawPlaylist) {
|
||||
insertInfo(rawPlaylist.playlistInfo)
|
||||
insertSongs(rawPlaylist.songs)
|
||||
insertRefs(
|
||||
|
@ -83,7 +83,7 @@ interface PlaylistDao {
|
|||
* @param playlistInfo The new [PlaylistInfo] to store.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun replacePlaylistInfo(playlistInfo: PlaylistInfo) {
|
||||
open suspend fun replacePlaylistInfo(playlistInfo: PlaylistInfo) {
|
||||
deleteInfo(playlistInfo.playlistUid)
|
||||
insertInfo(playlistInfo)
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ interface PlaylistDao {
|
|||
* @param playlistUid The [Music.UID] of the playlist to delete.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun deletePlaylist(playlistUid: Music.UID) {
|
||||
open suspend fun deletePlaylist(playlistUid: Music.UID) {
|
||||
deleteInfo(playlistUid)
|
||||
deleteRefs(playlistUid)
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ interface PlaylistDao {
|
|||
* @param songs The [PlaylistSong] representing each song to put into the playlist.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun insertPlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||
open suspend fun insertPlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||
insertSongs(songs)
|
||||
insertRefs(
|
||||
songs.map { PlaylistSongCrossRef(playlistUid = playlistUid, songUid = it.songUid) })
|
||||
|
@ -120,7 +120,7 @@ interface PlaylistDao {
|
|||
* playlist.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun replacePlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||
open suspend fun replacePlaylistSongs(playlistUid: Music.UID, songs: List<PlaylistSong>) {
|
||||
deleteRefs(playlistUid)
|
||||
insertSongs(songs)
|
||||
insertRefs(
|
||||
|
@ -128,21 +128,22 @@ interface PlaylistDao {
|
|||
}
|
||||
|
||||
/** Internal, do not use. */
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insertInfo(info: PlaylistInfo)
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
abstract suspend fun insertInfo(info: PlaylistInfo)
|
||||
|
||||
/** Internal, do not use. */
|
||||
@Query("DELETE FROM PlaylistInfo where playlistUid = :playlistUid")
|
||||
suspend fun deleteInfo(playlistUid: Music.UID)
|
||||
abstract suspend fun deleteInfo(playlistUid: Music.UID)
|
||||
|
||||
/** Internal, do not use. */
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
suspend fun insertSongs(songs: List<PlaylistSong>)
|
||||
abstract suspend fun insertSongs(songs: List<PlaylistSong>)
|
||||
|
||||
/** Internal, do not use. */
|
||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||
suspend fun insertRefs(refs: List<PlaylistSongCrossRef>)
|
||||
abstract suspend fun insertRefs(refs: List<PlaylistSongCrossRef>)
|
||||
|
||||
/** Internal, do not use. */
|
||||
@Query("DELETE FROM PlaylistSongCrossRef where playlistUid = :playlistUid")
|
||||
suspend fun deleteRefs(playlistUid: Music.UID)
|
||||
abstract suspend fun deleteRefs(playlistUid: Music.UID)
|
||||
}
|
||||
|
|
|
@ -32,5 +32,6 @@ interface PlaybackModule {
|
|||
@Singleton
|
||||
@Binds
|
||||
fun stateManager(playbackManager: PlaybackStateManagerImpl): PlaybackStateManager
|
||||
|
||||
@Binds fun settings(playbackSettings: PlaybackSettingsImpl): PlaybackSettings
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ constructor(
|
|||
/** The currently playing song. */
|
||||
val song: StateFlow<Song?>
|
||||
get() = _song
|
||||
|
||||
private val _parent = MutableStateFlow<MusicParent?>(null)
|
||||
/** The [MusicParent] currently being played. Null if playback is occurring from all songs. */
|
||||
val parent: StateFlow<MusicParent?> = _parent
|
||||
|
@ -74,6 +75,7 @@ constructor(
|
|||
/** Whether playback is ongoing or paused. */
|
||||
val isPlaying: StateFlow<Boolean>
|
||||
get() = _isPlaying
|
||||
|
||||
private val _positionDs = MutableStateFlow(0L)
|
||||
/** The current position, in deci-seconds (1/10th of a second). */
|
||||
val positionDs: StateFlow<Long>
|
||||
|
@ -83,6 +85,7 @@ constructor(
|
|||
/** The current [RepeatMode]. */
|
||||
val repeatMode: StateFlow<RepeatMode>
|
||||
get() = _repeatMode
|
||||
|
||||
private val _isShuffled = MutableStateFlow(false)
|
||||
/** Whether the queue is shuffled or not. */
|
||||
val isShuffled: StateFlow<Boolean>
|
||||
|
|
|
@ -114,12 +114,14 @@ class MutableQueue : Queue {
|
|||
@Volatile
|
||||
override var index = -1
|
||||
private set
|
||||
|
||||
override val currentSong: Song?
|
||||
get() =
|
||||
shuffledMapping
|
||||
.ifEmpty { orderedMapping.ifEmpty { null } }
|
||||
?.getOrNull(index)
|
||||
?.let(heap::get)
|
||||
|
||||
override val isShuffled: Boolean
|
||||
get() = shuffledMapping.isNotEmpty()
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.media3.common.C
|
|||
import androidx.media3.common.Format
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.audio.AudioProcessor
|
||||
import androidx.media3.exoplayer.audio.BaseAudioProcessor
|
||||
import androidx.media3.common.audio.BaseAudioProcessor
|
||||
import java.nio.ByteBuffer
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.pow
|
||||
|
@ -81,6 +81,7 @@ constructor(
|
|||
applyReplayGain(queue.currentSong)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewPlayback(queue: Queue, parent: MusicParent?) {
|
||||
logD("New playback started, updating playback information")
|
||||
applyReplayGain(queue.currentSong)
|
||||
|
|
|
@ -309,15 +309,18 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
|
|||
@Volatile
|
||||
override var parent: MusicParent? = null
|
||||
private set
|
||||
|
||||
@Volatile
|
||||
override var playerState = InternalPlayer.State.from(isPlaying = false, isAdvancing = false, 0)
|
||||
private set
|
||||
|
||||
@Volatile
|
||||
override var repeatMode = RepeatMode.NONE
|
||||
set(value) {
|
||||
field = value
|
||||
notifyRepeatModeChanged()
|
||||
}
|
||||
|
||||
override val currentAudioSessionId: Int?
|
||||
get() = internalPlayer?.audioSessionId
|
||||
|
||||
|
|
|
@ -27,5 +27,6 @@ import dagger.hilt.components.SingletonComponent
|
|||
@InstallIn(SingletonComponent::class)
|
||||
interface SearchModule {
|
||||
@Binds fun engine(searchEngine: SearchEngineImpl): SearchEngine
|
||||
|
||||
@Binds fun settings(searchSettings: SearchSettingsImpl): SearchSettings
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.util.fixDoubleRipple
|
|||
class IntListPreferenceDialog : PreferenceDialogFragmentCompat() {
|
||||
private val listPreference: IntListPreference
|
||||
get() = (preference as IntListPreference)
|
||||
|
||||
private var pendingValueIndex = -1
|
||||
|
||||
override fun onStart() {
|
||||
|
|
|
@ -76,6 +76,7 @@ abstract class BaseBottomSheetBehavior<V : View>(context: Context, attributeSet:
|
|||
|
||||
// Enable experimental settings that allow us to skip the half-expanded state.
|
||||
override fun shouldSkipHalfExpandedStateWhenDragging() = true
|
||||
|
||||
override fun shouldExpandOnUpwardDrag(dragDurationMillis: Long, yPositionPercentage: Float) =
|
||||
true
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ interface Event<T> {
|
|||
*/
|
||||
class MutableEvent<T> : Event<T> {
|
||||
override val flow = MutableStateFlow<T?>(null)
|
||||
|
||||
override fun consume() = flow.value?.also { flow.value = null }
|
||||
|
||||
/**
|
||||
|
|
|
@ -137,13 +137,18 @@ constructor(
|
|||
|
||||
// Respond to all major song or player changes that will affect the widget
|
||||
override fun onIndexMoved(queue: Queue) = update()
|
||||
|
||||
override fun onQueueReordered(queue: Queue) = update()
|
||||
|
||||
override fun onNewPlayback(queue: Queue, parent: MusicParent?) = update()
|
||||
|
||||
override fun onStateChanged(state: InternalPlayer.State) = update()
|
||||
|
||||
override fun onRepeatChanged(repeatMode: RepeatMode) = update()
|
||||
|
||||
// Respond to settings changes that will affect the widget
|
||||
override fun onRoundModeChanged() = update()
|
||||
|
||||
override fun onImageSettingsChanged() = update()
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,30 +29,43 @@ import org.oxycblt.auxio.music.info.ReleaseType
|
|||
open class FakeSong : Song {
|
||||
override val name: Name
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val date: Date?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val dateAdded: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val disc: Disc?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val genres: List<Genre>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val mimeType: MimeType
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val track: Int?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val path: Path
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val size: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val uri: Uri
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val album: Album
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val artists: List<Artist>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val durationMs: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val uid: Music.UID
|
||||
get() = throw NotImplementedError()
|
||||
}
|
||||
|
@ -60,20 +73,28 @@ open class FakeSong : Song {
|
|||
open class FakeAlbum : Album {
|
||||
override val name: Name
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val coverUri: Uri
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val dateAdded: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val dates: Date.Range?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val releaseType: ReleaseType
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val artists: List<Artist>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val durationMs: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val songs: List<Song>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val uid: Music.UID
|
||||
get() = throw NotImplementedError()
|
||||
}
|
||||
|
@ -81,18 +102,25 @@ open class FakeAlbum : Album {
|
|||
open class FakeArtist : Artist {
|
||||
override val name: Name
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val albums: List<Album>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val explicitAlbums: List<Album>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val implicitAlbums: List<Album>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val genres: List<Genre>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val durationMs: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val songs: List<Song>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val uid: Music.UID
|
||||
get() = throw NotImplementedError()
|
||||
}
|
||||
|
@ -100,12 +128,16 @@ open class FakeArtist : Artist {
|
|||
open class FakeGenre : Genre {
|
||||
override val name: Name
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val artists: List<Artist>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val durationMs: Long
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val songs: List<Song>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val uid: Music.UID
|
||||
get() = throw NotImplementedError()
|
||||
}
|
||||
|
|
|
@ -25,8 +25,10 @@ import org.oxycblt.auxio.music.user.UserLibrary
|
|||
open class FakeMusicRepository : MusicRepository {
|
||||
override val indexingState: IndexingState?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val deviceLibrary: DeviceLibrary?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val userLibrary: UserLibrary?
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
|
|
|
@ -23,40 +23,54 @@ import org.oxycblt.auxio.music.fs.MusicDirectories
|
|||
|
||||
open class FakeMusicSettings : MusicSettings {
|
||||
override fun registerListener(listener: MusicSettings.Listener) = throw NotImplementedError()
|
||||
|
||||
override fun unregisterListener(listener: MusicSettings.Listener) = throw NotImplementedError()
|
||||
|
||||
override var musicDirs: MusicDirectories
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override val excludeNonMusic: Boolean
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val shouldBeObserving: Boolean
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override var multiValueSeparators: String
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override val intelligentSorting: Boolean
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override var songSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var albumSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var artistSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var genreSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var playlistSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var albumSongSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var artistSongSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
||||
override var genreSongSort: Sort
|
||||
get() = throw NotImplementedError()
|
||||
set(_) = throw NotImplementedError()
|
||||
|
|
|
@ -77,6 +77,7 @@ class MusicViewModelTest {
|
|||
updateListener?.onMusicChanges(
|
||||
MusicRepository.Changes(deviceLibrary = true, userLibrary = false))
|
||||
}
|
||||
|
||||
override var indexingState: IndexingState? = null
|
||||
set(value) {
|
||||
field = value
|
||||
|
@ -114,10 +115,13 @@ class MusicViewModelTest {
|
|||
private class TestDeviceLibrary : FakeDeviceLibrary() {
|
||||
override val songs: List<Song>
|
||||
get() = listOf(TestSong(), TestSong())
|
||||
|
||||
override val albums: List<Album>
|
||||
get() = listOf(FakeAlbum(), FakeAlbum(), FakeAlbum())
|
||||
|
||||
override val artists: List<Artist>
|
||||
get() = listOf(FakeArtist(), FakeArtist(), FakeArtist(), FakeArtist())
|
||||
|
||||
override val genres: List<Genre>
|
||||
get() = listOf(FakeGenre())
|
||||
}
|
||||
|
|
|
@ -29,10 +29,13 @@ import org.oxycblt.auxio.music.Song
|
|||
open class FakeDeviceLibrary : DeviceLibrary {
|
||||
override val songs: List<Song>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val albums: List<Album>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val artists: List<Artist>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
override val genres: List<Genre>
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
|
|
28
build.gradle
28
build.gradle
|
@ -2,33 +2,23 @@ buildscript {
|
|||
ext {
|
||||
kotlin_version = '1.9.0'
|
||||
navigation_version = "2.6.0"
|
||||
hilt_version = '2.46.1'
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
hilt_version = '2.47'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.0.2'
|
||||
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.18.0"
|
||||
// Hilt isn't compatible with the new plugin syntax yet.
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
plugins {
|
||||
id "com.android.application" version "8.1.0" apply false
|
||||
id "androidx.navigation.safeargs.kotlin" version "$navigation_version" apply false
|
||||
id "org.jetbrains.kotlin.android" version "$kotlin_version" apply false
|
||||
id "com.google.devtools.ksp" version '1.9.0-1.0.12' apply false
|
||||
id "com.diffplug.spotless" version "6.20.0" apply false
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,7 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
distributionSha256Sum=a8451eeda314d0568b5340498b36edf147a8f0d692c5ff58082d477abe9146e4
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
include ':app'
|
||||
rootProject.name = "Auxio"
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
gradle.ext.androidxMediaModulePrefix = 'media-'
|
||||
gradle.ext.androidxMediaProjectName = 'media-'
|
||||
apply from: file("media/core_settings.gradle")
|
||||
apply from: file("media/core_settings.gradle")
|
||||
|
||||
rootProject.name = "Auxio"
|
||||
include ':app'
|
Loading…
Reference in a new issue