musikr: remove trivial auxio dependence
There's still some thorny resource use left over, but this is a good starting point to start breaking off musikr from auxio.
This commit is contained in:
parent
3da9e6c5b3
commit
de1c091517
39 changed files with 324 additions and 203 deletions
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.menu.Menu
|
import org.oxycblt.auxio.list.menu.Menu
|
||||||
import org.oxycblt.auxio.music.PlaylistDecision
|
import org.oxycblt.auxio.music.PlaylistDecision
|
||||||
import org.oxycblt.auxio.music.PlaylistMessage
|
import org.oxycblt.auxio.music.PlaylistMessage
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.PlaybackDecision
|
import org.oxycblt.auxio.playback.PlaybackDecision
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.util.collect
|
import org.oxycblt.auxio.util.collect
|
||||||
|
@ -43,7 +44,6 @@ import org.oxycblt.musikr.Album
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.MusicParent
|
import org.oxycblt.musikr.MusicParent
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.menu.Menu
|
import org.oxycblt.auxio.list.menu.Menu
|
||||||
import org.oxycblt.auxio.music.PlaylistDecision
|
import org.oxycblt.auxio.music.PlaylistDecision
|
||||||
import org.oxycblt.auxio.music.PlaylistMessage
|
import org.oxycblt.auxio.music.PlaylistMessage
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.PlaybackDecision
|
import org.oxycblt.auxio.playback.PlaybackDecision
|
||||||
import org.oxycblt.auxio.util.collect
|
import org.oxycblt.auxio.util.collect
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
@ -43,7 +44,6 @@ import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.MusicParent
|
import org.oxycblt.musikr.MusicParent
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.oxycblt.auxio.databinding.DialogSongDetailBinding
|
||||||
import org.oxycblt.auxio.detail.list.SongProperty
|
import org.oxycblt.auxio.detail.list.SongProperty
|
||||||
import org.oxycblt.auxio.detail.list.SongPropertyAdapter
|
import org.oxycblt.auxio.detail.list.SongPropertyAdapter
|
||||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.playback.replaygain.formatDb
|
import org.oxycblt.auxio.playback.replaygain.formatDb
|
||||||
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
|
||||||
|
@ -40,7 +41,6 @@ import org.oxycblt.auxio.util.concatLocalized
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.metadata.AudioProperties
|
import org.oxycblt.musikr.metadata.AudioProperties
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import org.oxycblt.musikr.tag.Name
|
import org.oxycblt.musikr.tag.Name
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,12 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
||||||
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
||||||
import org.oxycblt.auxio.list.recycler.MaterialDragCallback
|
import org.oxycblt.auxio.list.recycler.MaterialDragCallback
|
||||||
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.util.context
|
import org.oxycblt.auxio.util.context
|
||||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
import org.oxycblt.musikr.Playlist
|
import org.oxycblt.musikr.Playlist
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,6 @@ import android.view.ViewGroup
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.ItemSongPropertyBinding
|
import org.oxycblt.auxio.databinding.ItemSongPropertyBinding
|
||||||
import org.oxycblt.auxio.list.Item
|
|
||||||
import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
|
import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
|
||||||
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
||||||
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
||||||
|
@ -53,7 +52,7 @@ class SongPropertyAdapter :
|
||||||
* @param value The value of the property.
|
* @param value The value of the property.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
data class SongProperty(@StringRes val name: Int, val value: String) : Item
|
data class SongProperty(@StringRes val name: Int, val value: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.ViewHolder] that displays a [SongProperty]. Use [from] to create an instance.
|
* A [RecyclerView.ViewHolder] that displays a [SongProperty]. Use [from] to create an instance.
|
||||||
|
|
|
@ -22,9 +22,9 @@ import androidx.annotation.StringRes
|
||||||
|
|
||||||
// TODO: Consider breaking this up into sealed classes for individual adapters
|
// TODO: Consider breaking this up into sealed classes for individual adapters
|
||||||
/** A marker for something that is a RecyclerView item. Has no functionality on it's own. */
|
/** A marker for something that is a RecyclerView item. Has no functionality on it's own. */
|
||||||
interface Item
|
typealias Item = Any
|
||||||
|
|
||||||
interface Header : Item
|
interface Header
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A "header" used for delimiting groups of data.
|
* A "header" used for delimiting groups of data.
|
||||||
|
@ -44,7 +44,7 @@ interface PlainHeader : Header {
|
||||||
*/
|
*/
|
||||||
data class BasicHeader(@StringRes override val titleRes: Int) : PlainHeader
|
data class BasicHeader(@StringRes override val titleRes: Int) : PlainHeader
|
||||||
|
|
||||||
interface Divider<T> : Item {
|
interface Divider<T> {
|
||||||
val anchor: T?
|
val anchor: T?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.oxycblt.auxio.databinding.DialogMenuBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
import org.oxycblt.auxio.list.ListViewModel
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.util.getPlural
|
import org.oxycblt.auxio.util.getPlural
|
||||||
|
@ -37,7 +38,6 @@ import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Genre
|
import org.oxycblt.musikr.Genre
|
||||||
import org.oxycblt.musikr.Playlist
|
import org.oxycblt.musikr.Playlist
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [MenuDialogFragment] implementation for a [Song].
|
* [MenuDialogFragment] implementation for a [Song].
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.oxycblt.auxio.list.PlainDivider
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
||||||
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
||||||
|
import org.oxycblt.auxio.music.areNamesTheSame
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.util.context
|
import org.oxycblt.auxio.util.context
|
||||||
import org.oxycblt.auxio.util.getPlural
|
import org.oxycblt.auxio.util.getPlural
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
@ -40,8 +42,6 @@ import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Genre
|
import org.oxycblt.musikr.Genre
|
||||||
import org.oxycblt.musikr.Playlist
|
import org.oxycblt.musikr.Playlist
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.areNamesTheSame
|
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [RecyclerView.ViewHolder] that displays a [Song]. Use [from] to create an instance.
|
* A [RecyclerView.ViewHolder] that displays a [Song]. Use [from] to create an instance.
|
||||||
|
|
53
app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt
Normal file
53
app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Auxio Project
|
||||||
|
* MusicUtil.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlin.math.max
|
||||||
|
import org.oxycblt.auxio.util.concatLocalized
|
||||||
|
import org.oxycblt.musikr.Music
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run [Name.resolve] on each instance in the given list and concatenate them into a [String] in a
|
||||||
|
* localized manner.
|
||||||
|
*
|
||||||
|
* @param context [Context] required
|
||||||
|
* @return A concatenated string.
|
||||||
|
*/
|
||||||
|
fun <T : Music> List<T>.resolveNames(context: Context) =
|
||||||
|
concatLocalized(context) { it.name.resolve(context) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if [Music.name] matches for each item in a list. Useful for scenarios where the display
|
||||||
|
* information of an item must be compared without a context.
|
||||||
|
*
|
||||||
|
* @param other The list of items to compare to.
|
||||||
|
* @return True if they are the same (by [Music.name]), false otherwise.
|
||||||
|
*/
|
||||||
|
fun <T : Music> List<T>.areNamesTheSame(other: List<T>): Boolean {
|
||||||
|
for (i in 0 until max(size, other.size)) {
|
||||||
|
val a = getOrNull(i) ?: return false
|
||||||
|
val b = other.getOrNull(i) ?: return false
|
||||||
|
if (a.name != b.name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Auxio Project
|
||||||
|
* ReplayGainPreAmp.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current ReplayGain pre-amp configuration.
|
||||||
|
*
|
||||||
|
* @param with The pre-amp (in dB) to use when ReplayGain tags are present.
|
||||||
|
* @param without The pre-amp (in dB) to use when ReplayGain tags are not present.
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
data class ReplayGainPreAmp(val with: Float, val without: Float)
|
|
@ -25,7 +25,6 @@ import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.list.Item
|
|
||||||
import org.oxycblt.auxio.list.sort.Sort
|
import org.oxycblt.auxio.list.sort.Sort
|
||||||
import org.oxycblt.auxio.music.MusicRepository
|
import org.oxycblt.auxio.music.MusicRepository
|
||||||
import org.oxycblt.auxio.music.PlaylistDecision
|
import org.oxycblt.auxio.music.PlaylistDecision
|
||||||
|
@ -354,4 +353,4 @@ sealed interface ChosenName {
|
||||||
* [Playlist].
|
* [Playlist].
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
data class PlaylistChoice(val playlist: Playlist, val alreadyAdded: Boolean) : Item
|
data class PlaylistChoice(val playlist: Playlist, val alreadyAdded: Boolean)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.annotation.StringRes
|
||||||
import androidx.media.utils.MediaConstants
|
import androidx.media.utils.MediaConstants
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.formatDurationDs
|
import org.oxycblt.auxio.playback.formatDurationDs
|
||||||
import org.oxycblt.auxio.util.getPlural
|
import org.oxycblt.auxio.util.getPlural
|
||||||
import org.oxycblt.musikr.Album
|
import org.oxycblt.musikr.Album
|
||||||
|
@ -36,7 +37,6 @@ import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.MusicParent
|
import org.oxycblt.musikr.MusicParent
|
||||||
import org.oxycblt.musikr.Playlist
|
import org.oxycblt.musikr.Playlist
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
|
|
||||||
sealed interface MediaSessionUID {
|
sealed interface MediaSessionUID {
|
||||||
data class Tab(val node: TabNode) : MediaSessionUID {
|
data class Tab(val node: TabNode) : MediaSessionUID {
|
||||||
|
|
|
@ -26,13 +26,13 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||||
import org.oxycblt.auxio.util.getColorCompat
|
import org.oxycblt.auxio.util.getColorCompat
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
import org.oxycblt.auxio.list.ListViewModel
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||||
import org.oxycblt.auxio.playback.ui.SwipeCoverView
|
import org.oxycblt.auxio.playback.ui.SwipeCoverView
|
||||||
|
@ -44,7 +45,6 @@ import org.oxycblt.auxio.util.showToast
|
||||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
import org.oxycblt.musikr.MusicParent
|
import org.oxycblt.musikr.MusicParent
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,8 +24,8 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.music.ReplayGainPreAmp
|
||||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
|
import org.oxycblt.auxio.playback.replaygain.ReplayGainMode
|
||||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp
|
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
|
|
|
@ -32,11 +32,11 @@ import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
|
||||||
import org.oxycblt.auxio.list.adapter.PlayingIndicatorAdapter
|
import org.oxycblt.auxio.list.adapter.PlayingIndicatorAdapter
|
||||||
import org.oxycblt.auxio.list.recycler.MaterialDragCallback
|
import org.oxycblt.auxio.list.recycler.MaterialDragCallback
|
||||||
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
import org.oxycblt.auxio.list.recycler.SongViewHolder
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.util.context
|
import org.oxycblt.auxio.util.context
|
||||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogPreAmpBinding
|
import org.oxycblt.auxio.databinding.DialogPreAmpBinding
|
||||||
|
import org.oxycblt.auxio.music.ReplayGainPreAmp
|
||||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||||
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 Auxio Project
|
* Copyright (c) 2024 Auxio Project
|
||||||
* ReplayGain.kt is part of Auxio.
|
* ReplayGainMode.kt is part of Auxio.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,10 +18,7 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.playback.replaygain
|
package org.oxycblt.auxio.playback.replaygain
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlin.math.abs
|
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current ReplayGain configuration.
|
* The current ReplayGain configuration.
|
||||||
|
@ -55,34 +52,3 @@ enum class ReplayGainMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a ReplayGain adjustment to apply during song playback.
|
|
||||||
*
|
|
||||||
* @param track The track-specific adjustment that should be applied. Null if not available.
|
|
||||||
* @param album A more general album-specific adjustment that should be applied. Null if not
|
|
||||||
* available.
|
|
||||||
*/
|
|
||||||
data class ReplayGainAdjustment(val track: Float?, val album: Float?)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current ReplayGain pre-amp configuration.
|
|
||||||
*
|
|
||||||
* @param with The pre-amp (in dB) to use when ReplayGain tags are present.
|
|
||||||
* @param without The pre-amp (in dB) to use when ReplayGain tags are not present.
|
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
|
||||||
*/
|
|
||||||
data class ReplayGainPreAmp(val with: Float, val without: Float)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a decibel value in a human-readable format.
|
|
||||||
*
|
|
||||||
* @param context The context to resolve resources from.
|
|
||||||
* @return A formatted decibel value. Will be prefixed by a + or - sign.
|
|
||||||
*/
|
|
||||||
fun Float.formatDb(context: Context) =
|
|
||||||
if (this >= 0) {
|
|
||||||
context.getString(R.string.fmt_db_pos, this)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.fmt_db_neg, abs(this))
|
|
||||||
}
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Auxio Project
|
||||||
|
* ReplayGainUtil.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.oxycblt.auxio.playback.replaygain
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlin.math.abs
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a decibel value in a human-readable format.
|
||||||
|
*
|
||||||
|
* @param context The context to resolve resources from.
|
||||||
|
* @return A formatted decibel value. Will be prefixed by a + or - sign.
|
||||||
|
*/
|
||||||
|
fun Float.formatDb(context: Context) =
|
||||||
|
if (this >= 0) {
|
||||||
|
context.getString(R.string.fmt_db_pos, this)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.fmt_db_neg, abs(this))
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.image.BitmapProvider
|
import org.oxycblt.auxio.image.BitmapProvider
|
||||||
import org.oxycblt.auxio.image.ImageSettings
|
import org.oxycblt.auxio.image.ImageSettings
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.music.service.MediaSessionUID
|
import org.oxycblt.auxio.music.service.MediaSessionUID
|
||||||
import org.oxycblt.auxio.music.service.toMediaDescription
|
import org.oxycblt.auxio.music.service.toMediaDescription
|
||||||
import org.oxycblt.auxio.playback.ActionMode
|
import org.oxycblt.auxio.playback.ActionMode
|
||||||
|
@ -48,7 +49,6 @@ import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
||||||
import org.oxycblt.auxio.util.newMainPendingIntent
|
import org.oxycblt.auxio.util.newMainPendingIntent
|
||||||
import org.oxycblt.musikr.MusicParent
|
import org.oxycblt.musikr.MusicParent
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,11 +18,8 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.util
|
package org.oxycblt.auxio.util
|
||||||
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.util.UUID
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.musikr.tag.Date
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitizes a value that is unlikely to be null. On debug builds, this aliases to [requireNotNull],
|
* Sanitizes a value that is unlikely to be null. On debug builds, this aliases to [requireNotNull],
|
||||||
|
@ -43,28 +40,6 @@ fun <T> unlikelyToBeNull(value: T?) =
|
||||||
*/
|
*/
|
||||||
fun Int.positiveOrNull() = if (this > 0) this else null
|
fun Int.positiveOrNull() = if (this > 0) this else null
|
||||||
|
|
||||||
/**
|
|
||||||
* Aliases a check to ensure that the given number is non-zero.
|
|
||||||
*
|
|
||||||
* @return The same number if it's non-zero, null otherwise.
|
|
||||||
*/
|
|
||||||
fun Long.positiveOrNull() = if (this > 0) this else null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aliases a check to ensure that the given number is non-zero.
|
|
||||||
*
|
|
||||||
* @return The same number if it's non-zero, null otherwise.
|
|
||||||
*/
|
|
||||||
fun Float.nonZeroOrNull() = if (this != 0f) this else null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aliases a check to ensure a given value is in a specified range.
|
|
||||||
*
|
|
||||||
* @param range The valid range of values for this number.
|
|
||||||
* @return The same number if it is in the range, null otherwise.
|
|
||||||
*/
|
|
||||||
fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily set up a reflected field. Automatically handles visibility changes. Adapted from Material
|
* Lazily set up a reflected field. Automatically handles visibility changes. Adapted from Material
|
||||||
* Files: https://github.com/zhanghai/MaterialFiles
|
* Files: https://github.com/zhanghai/MaterialFiles
|
||||||
|
@ -75,6 +50,7 @@ fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else nul
|
||||||
fun lazyReflectedField(clazz: KClass<*>, field: String) = lazy {
|
fun lazyReflectedField(clazz: KClass<*>, field: String) = lazy {
|
||||||
clazz.java.getDeclaredField(field).also { it.isAccessible = true }
|
clazz.java.getDeclaredField(field).also { it.isAccessible = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily set up a reflected method. Automatically handles visibility changes. Adapted from Material
|
* Lazily set up a reflected method. Automatically handles visibility changes. Adapted from Material
|
||||||
* Files: https://github.com/zhanghai/MaterialFiles
|
* Files: https://github.com/zhanghai/MaterialFiles
|
||||||
|
@ -87,64 +63,3 @@ fun lazyReflectedMethod(clazz: KClass<*>, method: String, vararg params: KClass<
|
||||||
it.isAccessible = true
|
it.isAccessible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a [String] to a [UUID].
|
|
||||||
*
|
|
||||||
* @return A [UUID] converted from the [String] value, or null if the value was not valid.
|
|
||||||
* @see UUID.fromString
|
|
||||||
*/
|
|
||||||
fun String.toUuidOrNull(): UUID? =
|
|
||||||
try {
|
|
||||||
UUID.fromString(this)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a [MessageDigest] with a lowercase [String].
|
|
||||||
*
|
|
||||||
* @param string The [String] to hash. If null, it will not be hashed.
|
|
||||||
*/
|
|
||||||
fun MessageDigest.update(string: String?) {
|
|
||||||
if (string != null) {
|
|
||||||
update(string.lowercase().toByteArray())
|
|
||||||
} else {
|
|
||||||
update(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a [MessageDigest] with the string representation of a [Date].
|
|
||||||
*
|
|
||||||
* @param date The [Date] to hash. If null, nothing will be done.
|
|
||||||
*/
|
|
||||||
fun MessageDigest.update(date: Date?) {
|
|
||||||
if (date != null) {
|
|
||||||
update(date.toString().toByteArray())
|
|
||||||
} else {
|
|
||||||
update(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a [MessageDigest] with the lowercase versions of all of the input [String]s.
|
|
||||||
*
|
|
||||||
* @param strings The [String]s to hash. If a [String] is null, it will not be hashed.
|
|
||||||
*/
|
|
||||||
fun MessageDigest.update(strings: List<String?>) {
|
|
||||||
strings.forEach(::update)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a [MessageDigest] with the little-endian bytes of a [Int].
|
|
||||||
*
|
|
||||||
* @param n The [Int] to write. If null, nothing will be done.
|
|
||||||
*/
|
|
||||||
fun MessageDigest.update(n: Int?) {
|
|
||||||
if (n != null) {
|
|
||||||
update(byteArrayOf(n.toByte(), n.shr(8).toByte(), n.shr(16).toByte(), n.shr(24).toByte()))
|
|
||||||
} else {
|
|
||||||
update(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,12 +30,12 @@ import android.view.View
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.playback.service.PlaybackActions
|
import org.oxycblt.auxio.playback.service.PlaybackActions
|
||||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||||
import org.oxycblt.auxio.ui.UISettings
|
import org.oxycblt.auxio.ui.UISettings
|
||||||
import org.oxycblt.auxio.ui.UISettingsImpl
|
import org.oxycblt.auxio.ui.UISettingsImpl
|
||||||
import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,20 +18,13 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr
|
package org.oxycblt.musikr
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.math.max
|
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.oxycblt.auxio.list.Item
|
|
||||||
import org.oxycblt.auxio.music.MusicType
|
|
||||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
|
||||||
import org.oxycblt.auxio.util.concatLocalized
|
|
||||||
import org.oxycblt.auxio.util.toUuidOrNull
|
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.fs.Format
|
import org.oxycblt.musikr.fs.Format
|
||||||
import org.oxycblt.musikr.fs.Path
|
import org.oxycblt.musikr.fs.Path
|
||||||
|
@ -39,6 +32,8 @@ import org.oxycblt.musikr.tag.Date
|
||||||
import org.oxycblt.musikr.tag.Disc
|
import org.oxycblt.musikr.tag.Disc
|
||||||
import org.oxycblt.musikr.tag.Name
|
import org.oxycblt.musikr.tag.Name
|
||||||
import org.oxycblt.musikr.tag.ReleaseType
|
import org.oxycblt.musikr.tag.ReleaseType
|
||||||
|
import org.oxycblt.musikr.tag.ReplayGainAdjustment
|
||||||
|
import org.oxycblt.musikr.util.toUuidOrNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract music data. This contains universal information about all concrete music
|
* Abstract music data. This contains universal information about all concrete music
|
||||||
|
@ -46,7 +41,7 @@ import org.oxycblt.musikr.tag.ReleaseType
|
||||||
*
|
*
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
sealed interface Music : Item {
|
sealed interface Music {
|
||||||
/**
|
/**
|
||||||
* A unique identifier for this music item.
|
* A unique identifier for this music item.
|
||||||
*
|
*
|
||||||
|
@ -189,7 +184,7 @@ sealed interface Music : Item {
|
||||||
* Creates a MusicBrainz-style [UID] with a [UUID] derived from the MusicBrainz ID
|
* Creates a MusicBrainz-style [UID] with a [UUID] derived from the MusicBrainz ID
|
||||||
* extracted from a file.
|
* extracted from a file.
|
||||||
*
|
*
|
||||||
* @param item The analogous [MusicType] of the item that created this [UID].
|
* @param item The [Item] that created this [UID].
|
||||||
* @param mbid The analogous MusicBrainz ID for this item that was extracted from a
|
* @param mbid The analogous MusicBrainz ID for this item that was extracted from a
|
||||||
* file.
|
* file.
|
||||||
* @return A new MusicBrainz-style [UID].
|
* @return A new MusicBrainz-style [UID].
|
||||||
|
@ -372,32 +367,3 @@ interface Playlist : MusicParent {
|
||||||
/** Useful information to quickly obtain a (single) cover for a Genre. */
|
/** Useful information to quickly obtain a (single) cover for a Genre. */
|
||||||
val cover: Cover
|
val cover: Cover
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Run [Name.resolve] on each instance in the given list and concatenate them into a [String] in a
|
|
||||||
* localized manner.
|
|
||||||
*
|
|
||||||
* @param context [Context] required
|
|
||||||
* @return A concatenated string.
|
|
||||||
*/
|
|
||||||
fun <T : Music> List<T>.resolveNames(context: Context) =
|
|
||||||
concatLocalized(context) { it.name.resolve(context) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if [Music.name] matches for each item in a list. Useful for scenarios where the display
|
|
||||||
* information of an item must be compared without a context.
|
|
||||||
*
|
|
||||||
* @param other The list of items to compare to.
|
|
||||||
* @return True if they are the same (by [Music.name]), false otherwise.
|
|
||||||
*/
|
|
||||||
fun <T : Music> List<T>.areNamesTheSame(other: List<T>): Boolean {
|
|
||||||
for (i in 0 until max(size, other.size)) {
|
|
||||||
val a = getOrNull(i) ?: return false
|
|
||||||
val b = other.getOrNull(i) ?: return false
|
|
||||||
if (a.name != b.name) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.cover
|
package org.oxycblt.musikr.cover
|
||||||
|
|
||||||
import org.oxycblt.auxio.list.sort.Sort
|
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
|
|
||||||
sealed interface Cover {
|
sealed interface Cover {
|
||||||
|
@ -31,8 +30,6 @@ sealed interface Cover {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val FALLBACK_SORT = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
|
|
||||||
|
|
||||||
fun nil() = Multi(listOf())
|
fun nil() = Multi(listOf())
|
||||||
|
|
||||||
fun single(key: String) = Single(key)
|
fun single(key: String) = Single(key)
|
||||||
|
@ -40,10 +37,11 @@ sealed interface Cover {
|
||||||
fun multi(songs: Collection<Song>) = order(songs).run { Multi(this) }
|
fun multi(songs: Collection<Song>) = order(songs).run { Multi(this) }
|
||||||
|
|
||||||
private fun order(songs: Collection<Song>) =
|
private fun order(songs: Collection<Song>) =
|
||||||
FALLBACK_SORT.songs(songs)
|
songs
|
||||||
.mapNotNull { it.cover }
|
.mapNotNull { it.cover }
|
||||||
.groupBy { it.key }
|
.groupBy { it.key }
|
||||||
.entries
|
.entries
|
||||||
|
.sortedByDescending { it.key }
|
||||||
.sortedByDescending { it.value.size }
|
.sortedByDescending { it.value.size }
|
||||||
.map { it.value.first() }
|
.map { it.value.first() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
package org.oxycblt.musikr.fs
|
package org.oxycblt.musikr.fs
|
||||||
|
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.musikr.util.unlikelyToBeNull
|
||||||
|
|
||||||
sealed interface Format {
|
sealed interface Format {
|
||||||
val mimeType: String
|
val mimeType: String
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.graph
|
package org.oxycblt.musikr.graph
|
||||||
|
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
||||||
import org.oxycblt.musikr.tag.interpret.PreArtist
|
import org.oxycblt.musikr.tag.interpret.PreArtist
|
||||||
import org.oxycblt.musikr.tag.interpret.PreGenre
|
import org.oxycblt.musikr.tag.interpret.PreGenre
|
||||||
import org.oxycblt.musikr.tag.interpret.PreSong
|
import org.oxycblt.musikr.tag.interpret.PreSong
|
||||||
|
import org.oxycblt.musikr.util.unlikelyToBeNull
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
data class MusicGraph(
|
data class MusicGraph(
|
||||||
|
|
|
@ -21,11 +21,11 @@ package org.oxycblt.musikr.metadata
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
|
||||||
import org.oxycblt.ktaglib.FileRef
|
import org.oxycblt.ktaglib.FileRef
|
||||||
import org.oxycblt.ktaglib.KTagLib
|
import org.oxycblt.ktaglib.KTagLib
|
||||||
import org.oxycblt.ktaglib.Metadata
|
import org.oxycblt.ktaglib.Metadata
|
||||||
import org.oxycblt.musikr.fs.query.DeviceFile
|
import org.oxycblt.musikr.fs.query.DeviceFile
|
||||||
|
import org.oxycblt.musikr.util.unlikelyToBeNull
|
||||||
|
|
||||||
interface MetadataExtractor {
|
interface MetadataExtractor {
|
||||||
suspend fun extract(file: DeviceFile): Metadata?
|
suspend fun extract(file: DeviceFile): Metadata?
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.model
|
package org.oxycblt.musikr.model
|
||||||
|
|
||||||
import org.oxycblt.auxio.util.update
|
|
||||||
import org.oxycblt.musikr.Album
|
import org.oxycblt.musikr.Album
|
||||||
import org.oxycblt.musikr.Artist
|
import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
|
@ -26,6 +25,7 @@ import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.tag.Date
|
import org.oxycblt.musikr.tag.Date
|
||||||
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
import org.oxycblt.musikr.tag.interpret.PreAlbum
|
||||||
|
import org.oxycblt.musikr.util.update
|
||||||
|
|
||||||
interface AlbumCore {
|
interface AlbumCore {
|
||||||
val preAlbum: PreAlbum
|
val preAlbum: PreAlbum
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.model
|
package org.oxycblt.musikr.model
|
||||||
|
|
||||||
import org.oxycblt.auxio.util.update
|
|
||||||
import org.oxycblt.musikr.Album
|
import org.oxycblt.musikr.Album
|
||||||
import org.oxycblt.musikr.Artist
|
import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Genre
|
import org.oxycblt.musikr.Genre
|
||||||
|
@ -26,6 +25,7 @@ import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.tag.interpret.PreArtist
|
import org.oxycblt.musikr.tag.interpret.PreArtist
|
||||||
|
import org.oxycblt.musikr.util.update
|
||||||
|
|
||||||
interface ArtistCore {
|
interface ArtistCore {
|
||||||
val preArtist: PreArtist
|
val preArtist: PreArtist
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.model
|
package org.oxycblt.musikr.model
|
||||||
|
|
||||||
import org.oxycblt.auxio.util.update
|
|
||||||
import org.oxycblt.musikr.Artist
|
import org.oxycblt.musikr.Artist
|
||||||
import org.oxycblt.musikr.Genre
|
import org.oxycblt.musikr.Genre
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.Song
|
import org.oxycblt.musikr.Song
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.tag.interpret.PreGenre
|
import org.oxycblt.musikr.tag.interpret.PreGenre
|
||||||
|
import org.oxycblt.musikr.util.update
|
||||||
|
|
||||||
interface GenreCore {
|
interface GenreCore {
|
||||||
val preGenre: PreGenre
|
val preGenre: PreGenre
|
||||||
|
|
|
@ -25,7 +25,7 @@ import java.io.BufferedWriter
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.musikr.Playlist
|
import org.oxycblt.musikr.Playlist
|
||||||
import org.oxycblt.musikr.fs.Components
|
import org.oxycblt.musikr.fs.Components
|
||||||
import org.oxycblt.musikr.fs.Path
|
import org.oxycblt.musikr.fs.Path
|
||||||
|
@ -34,8 +34,8 @@ import org.oxycblt.musikr.fs.path.VolumeManager
|
||||||
import org.oxycblt.musikr.playlist.ExportConfig
|
import org.oxycblt.musikr.playlist.ExportConfig
|
||||||
import org.oxycblt.musikr.playlist.ImportedPlaylist
|
import org.oxycblt.musikr.playlist.ImportedPlaylist
|
||||||
import org.oxycblt.musikr.playlist.PossiblePaths
|
import org.oxycblt.musikr.playlist.PossiblePaths
|
||||||
import org.oxycblt.musikr.resolveNames
|
|
||||||
import org.oxycblt.musikr.tag.util.correctWhitespace
|
import org.oxycblt.musikr.tag.util.correctWhitespace
|
||||||
|
import org.oxycblt.musikr.util.unlikelyToBeNull
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,8 +23,8 @@ import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.util.inRangeOrNull
|
import org.oxycblt.musikr.util.inRangeOrNull
|
||||||
import org.oxycblt.auxio.util.positiveOrNull
|
import org.oxycblt.musikr.util.positiveOrNull
|
||||||
import timber.log.Timber as L
|
import timber.log.Timber as L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.oxycblt.musikr.tag
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.list.Item
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A disc identifier for a song.
|
* A disc identifier for a song.
|
||||||
|
@ -28,7 +27,7 @@ import org.oxycblt.auxio.list.Item
|
||||||
* @param number The disc number.
|
* @param number The disc number.
|
||||||
* @param name The name of the disc group, if any. Null if not present.
|
* @param name The name of the disc group, if any. Null if not present.
|
||||||
*/
|
*/
|
||||||
class Disc(val number: Int, val name: String?) : Item, Comparable<Disc> {
|
class Disc(val number: Int, val name: String?) : Comparable<Disc> {
|
||||||
// We don't want to group discs by differing subtitles, so only compare by the number
|
// 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 equals(other: Any?) = other is Disc && number == other.number
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Auxio Project
|
||||||
|
* ReplayGainAdjustment.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* 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.musikr.tag
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a ReplayGain adjustment to apply during song playback.
|
||||||
|
*
|
||||||
|
* @param track The track-specific adjustment that should be applied. Null if not available.
|
||||||
|
* @param album A more general album-specific adjustment that should be applied. Null if not
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
data class ReplayGainAdjustment(val track: Float?, val album: Float?)
|
|
@ -20,8 +20,6 @@ package org.oxycblt.musikr.tag.interpret
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
|
||||||
import org.oxycblt.auxio.util.update
|
|
||||||
import org.oxycblt.musikr.Music
|
import org.oxycblt.musikr.Music
|
||||||
import org.oxycblt.musikr.cover.Cover
|
import org.oxycblt.musikr.cover.Cover
|
||||||
import org.oxycblt.musikr.fs.Format
|
import org.oxycblt.musikr.fs.Format
|
||||||
|
@ -31,6 +29,8 @@ import org.oxycblt.musikr.tag.Date
|
||||||
import org.oxycblt.musikr.tag.Disc
|
import org.oxycblt.musikr.tag.Disc
|
||||||
import org.oxycblt.musikr.tag.Name
|
import org.oxycblt.musikr.tag.Name
|
||||||
import org.oxycblt.musikr.tag.ReleaseType
|
import org.oxycblt.musikr.tag.ReleaseType
|
||||||
|
import org.oxycblt.musikr.tag.ReplayGainAdjustment
|
||||||
|
import org.oxycblt.musikr.util.update
|
||||||
|
|
||||||
data class PreSong(
|
data class PreSong(
|
||||||
val musicBrainzId: UUID?,
|
val musicBrainzId: UUID?,
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
package org.oxycblt.musikr.tag.interpret
|
package org.oxycblt.musikr.tag.interpret
|
||||||
|
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.playback.replaygain.ReplayGainAdjustment
|
|
||||||
import org.oxycblt.auxio.util.toUuidOrNull
|
|
||||||
import org.oxycblt.musikr.Interpretation
|
import org.oxycblt.musikr.Interpretation
|
||||||
import org.oxycblt.musikr.fs.Format
|
import org.oxycblt.musikr.fs.Format
|
||||||
import org.oxycblt.musikr.fs.query.DeviceFile
|
import org.oxycblt.musikr.fs.query.DeviceFile
|
||||||
|
@ -28,8 +26,10 @@ import org.oxycblt.musikr.pipeline.RawSong
|
||||||
import org.oxycblt.musikr.tag.Disc
|
import org.oxycblt.musikr.tag.Disc
|
||||||
import org.oxycblt.musikr.tag.Name
|
import org.oxycblt.musikr.tag.Name
|
||||||
import org.oxycblt.musikr.tag.ReleaseType
|
import org.oxycblt.musikr.tag.ReleaseType
|
||||||
|
import org.oxycblt.musikr.tag.ReplayGainAdjustment
|
||||||
import org.oxycblt.musikr.tag.parse.ParsedTags
|
import org.oxycblt.musikr.tag.parse.ParsedTags
|
||||||
import org.oxycblt.musikr.tag.util.parseId3GenreNames
|
import org.oxycblt.musikr.tag.util.parseId3GenreNames
|
||||||
|
import org.oxycblt.musikr.util.toUuidOrNull
|
||||||
|
|
||||||
interface TagInterpreter {
|
interface TagInterpreter {
|
||||||
fun interpret(song: RawSong, interpretation: Interpretation): PreSong
|
fun interpret(song: RawSong, interpretation: Interpretation): PreSong
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
package org.oxycblt.musikr.tag.parse
|
package org.oxycblt.musikr.tag.parse
|
||||||
|
|
||||||
import androidx.core.text.isDigitsOnly
|
import androidx.core.text.isDigitsOnly
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
|
||||||
import org.oxycblt.ktaglib.Metadata
|
import org.oxycblt.ktaglib.Metadata
|
||||||
import org.oxycblt.musikr.tag.Date
|
import org.oxycblt.musikr.tag.Date
|
||||||
import org.oxycblt.musikr.tag.util.parseId3v2PositionField
|
import org.oxycblt.musikr.tag.util.parseId3v2PositionField
|
||||||
import org.oxycblt.musikr.tag.util.parseXiphPositionField
|
import org.oxycblt.musikr.tag.util.parseXiphPositionField
|
||||||
|
import org.oxycblt.musikr.util.nonZeroOrNull
|
||||||
|
|
||||||
// Song
|
// Song
|
||||||
fun Metadata.musicBrainzId() =
|
fun Metadata.musicBrainzId() =
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
package org.oxycblt.musikr.tag.util
|
package org.oxycblt.musikr.tag.util
|
||||||
|
|
||||||
import org.oxycblt.auxio.util.positiveOrNull
|
import org.oxycblt.musikr.util.positiveOrNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an ID3v2-style position + total [String] field. These fields consist of a number and an
|
* Parse an ID3v2-style position + total [String] field. These fields consist of a number and an
|
||||||
|
|
133
app/src/main/java/org/oxycblt/musikr/util/LangUtil.kt
Normal file
133
app/src/main/java/org/oxycblt/musikr/util/LangUtil.kt
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Auxio Project
|
||||||
|
* LangUtil.kt is part of Auxio.
|
||||||
|
*
|
||||||
|
* 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.musikr.util
|
||||||
|
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.util.UUID
|
||||||
|
import org.oxycblt.auxio.BuildConfig
|
||||||
|
import org.oxycblt.musikr.tag.Date
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a value that is unlikely to be null. On debug builds, this aliases to [requireNotNull],
|
||||||
|
* otherwise, it aliases to the unchecked dereference operator (!!). This can be used as a minor
|
||||||
|
* optimization in certain cases.
|
||||||
|
*/
|
||||||
|
fun <T> unlikelyToBeNull(value: T?) =
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
requireNotNull(value)
|
||||||
|
} else {
|
||||||
|
value!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliases a check to ensure that the given number is non-zero.
|
||||||
|
*
|
||||||
|
* @return The given number if it's non-zero, null otherwise.
|
||||||
|
*/
|
||||||
|
fun Int.positiveOrNull() = if (this > 0) this else null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliases a check to ensure that the given number is non-zero.
|
||||||
|
*
|
||||||
|
* @return The same number if it's non-zero, null otherwise.
|
||||||
|
*/
|
||||||
|
fun Float.nonZeroOrNull() = if (this != 0f) this else null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliases a check to ensure a given value is in a specified range.
|
||||||
|
*
|
||||||
|
* @param range The valid range of values for this number.
|
||||||
|
* @return The same number if it is in the range, null otherwise.
|
||||||
|
*/
|
||||||
|
fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a [String] to a [UUID].
|
||||||
|
*
|
||||||
|
* @return A [UUID] converted from the [String] value, or null if the value was not valid.
|
||||||
|
* @see UUID.fromString
|
||||||
|
*/
|
||||||
|
fun String.toUuidOrNull(): UUID? =
|
||||||
|
try {
|
||||||
|
UUID.fromString(this)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a [MessageDigest] with a lowercase [String].
|
||||||
|
*
|
||||||
|
* @param string The [String] to hash. If null, it will not be hashed.
|
||||||
|
*/
|
||||||
|
fun MessageDigest.update(string: String?) {
|
||||||
|
if (string != null) {
|
||||||
|
update(string.lowercase().toByteArray())
|
||||||
|
} else {
|
||||||
|
update(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a [MessageDigest] with the string representation of a [Date].
|
||||||
|
*
|
||||||
|
* @param date The [Date] to hash. If null, nothing will be done.
|
||||||
|
*/
|
||||||
|
fun MessageDigest.update(date: Date?) {
|
||||||
|
if (date != null) {
|
||||||
|
update(date.toString().toByteArray())
|
||||||
|
} else {
|
||||||
|
update(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a [MessageDigest] with the lowercase versions of all of the input [String]s.
|
||||||
|
*
|
||||||
|
* @param strings The [String]s to hash. If a [String] is null, it will not be hashed.
|
||||||
|
*/
|
||||||
|
fun MessageDigest.update(strings: List<String?>) {
|
||||||
|
strings.forEach(::update)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a [MessageDigest] with the little-endian bytes of a [Int].
|
||||||
|
*
|
||||||
|
* @param n The [Int] to write. If null, nothing will be done.
|
||||||
|
*/
|
||||||
|
fun MessageDigest.update(n: Int?) {
|
||||||
|
if (n != null) {
|
||||||
|
update(byteArrayOf(n.toByte(), n.shr(8).toByte(), n.shr(16).toByte(), n.shr(24).toByte()))
|
||||||
|
} else {
|
||||||
|
update(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily set up a reflected method. Automatically handles visibility changes. Adapted from Material
|
||||||
|
* Files: https://github.com/zhanghai/MaterialFiles
|
||||||
|
*
|
||||||
|
* @param clazz The [KClass] to reflect into.
|
||||||
|
* @param method The name of the method to obtain.
|
||||||
|
*/
|
||||||
|
fun lazyReflectedMethod(clazz: KClass<*>, method: String, vararg params: KClass<*>) = lazy {
|
||||||
|
clazz.java.getDeclaredMethod(method, *params.map { it.java }.toTypedArray()).also {
|
||||||
|
it.isAccessible = true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue