all: reformat code
Reformat all project code
This commit is contained in:
parent
7212700553
commit
cce7b766d7
38 changed files with 194 additions and 203 deletions
|
@ -36,6 +36,7 @@ audio focus was lost
|
|||
- Fixed issue where the artist name would not be shown in the OS audio switcher menu
|
||||
- Fixed issue where the search view would not update if the library changed
|
||||
- Fixed visual bug with transitions in the black theme
|
||||
- Fixed toolbar flickering when fast-scrolling in the home UI
|
||||
|
||||
#### What's Changed
|
||||
- Ignore MediaStore tags is now Auxio's default and unchangeable behavior. The option has been removed.
|
||||
|
|
|
@ -24,7 +24,6 @@ android {
|
|||
}
|
||||
|
||||
// ExoPlayer, AndroidX, and Material Components all need Java 8 to compile.
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -108,6 +107,7 @@ dependencies {
|
|||
implementation "io.coil-kt:coil:2.1.0"
|
||||
|
||||
// Material
|
||||
// Locked below 1.7.0-alpha03 to avoid the same ripple bug
|
||||
implementation "com.google.android.material:material:1.7.0-alpha02"
|
||||
|
||||
// LeakCanary
|
||||
|
|
|
@ -121,6 +121,7 @@ class MainActivity : AppCompatActivity() {
|
|||
*/
|
||||
private fun startIntentAction(intent: Intent?): Boolean {
|
||||
if (intent == null) {
|
||||
// Nothing to do.
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,9 @@ import org.oxycblt.auxio.util.*
|
|||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class MainFragment :
|
||||
ViewBindingFragment<FragmentMainBinding>(), ViewTreeObserver.OnPreDrawListener, NavController.OnDestinationChangedListener {
|
||||
ViewBindingFragment<FragmentMainBinding>(),
|
||||
ViewTreeObserver.OnPreDrawListener,
|
||||
NavController.OnDestinationChangedListener {
|
||||
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
|
|
@ -155,7 +155,7 @@ class DetailViewModel(application: Application) :
|
|||
} else {
|
||||
_currentSong.value = null
|
||||
}
|
||||
logD("Updated song to ${newSong}")
|
||||
logD("Updated song to $newSong")
|
||||
}
|
||||
|
||||
val album = currentAlbum.value
|
||||
|
|
|
@ -105,18 +105,18 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite
|
|||
* @param genre The new [Song] to bind.
|
||||
* @param listener A [DetailAdapter.Listener] to bind interactions to.
|
||||
*/
|
||||
fun bind(item: Genre, listener: DetailAdapter.Listener) {
|
||||
binding.detailCover.bind(item)
|
||||
fun bind(genre: Genre, listener: DetailAdapter.Listener) {
|
||||
binding.detailCover.bind(genre)
|
||||
binding.detailType.text = binding.context.getString(R.string.lbl_genre)
|
||||
binding.detailName.text = item.resolveName(binding.context)
|
||||
binding.detailName.text = genre.resolveName(binding.context)
|
||||
// Nothing about a genre is applicable to the sub-head text.
|
||||
binding.detailSubhead.isVisible = false
|
||||
// The song count of the genre maps to the info text.
|
||||
binding.detailInfo.text =
|
||||
binding.context.getString(
|
||||
R.string.fmt_two,
|
||||
binding.context.getPlural(R.plurals.fmt_artist_count, item.artists.size),
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size))
|
||||
binding.context.getPlural(R.plurals.fmt_artist_count, genre.artists.size),
|
||||
binding.context.getPlural(R.plurals.fmt_song_count, genre.songs.size))
|
||||
binding.detailPlayButton.setOnClickListener { listener.onPlay() }
|
||||
binding.detailShuffleButton.setOnClickListener { listener.onShuffle() }
|
||||
}
|
||||
|
|
|
@ -426,7 +426,7 @@ class HomeFragment :
|
|||
when (item) {
|
||||
is Song -> HomeFragmentDirections.actionShowAlbum(item.album.uid)
|
||||
is Album -> HomeFragmentDirections.actionShowAlbum(item.uid)
|
||||
is Artist -> HomeFragmentDirections.actionShowArtist(item.uid.also { logD(it) })
|
||||
is Artist -> HomeFragmentDirections.actionShowArtist(item.uid)
|
||||
is Genre -> HomeFragmentDirections.actionShowGenre(item.uid)
|
||||
else -> return
|
||||
}
|
||||
|
|
|
@ -175,10 +175,8 @@ object Covers {
|
|||
* @return An [InputStream] of image data if the cover loading was successful, null otherwise.
|
||||
*/
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
private suspend fun fetchMediaStoreCovers(context: Context, data: Album): InputStream? {
|
||||
val uri = data.coverUri
|
||||
|
||||
private suspend fun fetchMediaStoreCovers(context: Context, album: Album): InputStream? {
|
||||
// Eliminate any chance that this blocking call might mess up the loading process
|
||||
return withContext(Dispatchers.IO) { context.contentResolver.openInputStream(uri) }
|
||||
return withContext(Dispatchers.IO) { context.contentResolver.openInputStream(album.coverUri) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ sealed class Music : Item {
|
|||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an auxio-style [UID] with a [UUID] composed of a hash of the non-subjective,
|
||||
* Creates an Auxio-style [UID] with a [UUID] composed of a hash of the non-subjective,
|
||||
* unlikely-to-change metadata of the music.
|
||||
* @param mode The analogous [MusicMode] of the item that created this [UID].
|
||||
* @param updates Block to update the [MessageDigest] hash with the metadata of the
|
||||
|
@ -194,7 +194,8 @@ sealed class Music : Item {
|
|||
}
|
||||
// Convert the digest to a UUID. This does cleave off some of the hash, but this
|
||||
// is considered okay.
|
||||
val uuid = UUID(
|
||||
val uuid =
|
||||
UUID(
|
||||
digest[0]
|
||||
.toLong()
|
||||
.shl(56)
|
||||
|
@ -224,7 +225,7 @@ sealed class Music : Item {
|
|||
* @param mode The analogous [MusicMode] of the item that created this [UID].
|
||||
* @param mbid The analogous MusicBrainz ID for this item that was extracted from a
|
||||
* file.
|
||||
* @return A new MusicBrainz-style [UID]
|
||||
* @return A new MusicBrainz-style [UID].
|
||||
*/
|
||||
fun musicBrainz(mode: MusicMode, mbid: UUID): UID = UID(Format.MUSICBRAINZ, mode, mbid)
|
||||
|
||||
|
@ -396,8 +397,7 @@ class Song constructor(raw: Raw, settings: Settings) : Music() {
|
|||
|
||||
/**
|
||||
* Resolves one or more [Artist]s into a single piece of human-readable names.
|
||||
* @param context [Context] required for [resolveName].
|
||||
* formatter.
|
||||
* @param context [Context] required for [resolveName]. formatter.
|
||||
*/
|
||||
fun resolveArtistContents(context: Context) =
|
||||
// TODO Internationalize the list
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.database.getIntOrNull
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import java.io.File
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.util.*
|
||||
|
@ -357,23 +356,15 @@ private class CacheDatabase(context: Context) :
|
|||
put(Columns.ALBUM_SORT_NAME, rawSong.albumSortName)
|
||||
put(Columns.ALBUM_TYPES, rawSong.albumTypes.toSQLMultiValue())
|
||||
|
||||
put(
|
||||
Columns.ARTIST_MUSIC_BRAINZ_IDS,
|
||||
rawSong.artistMusicBrainzIds.toSQLMultiValue())
|
||||
put(Columns.ARTIST_MUSIC_BRAINZ_IDS, rawSong.artistMusicBrainzIds.toSQLMultiValue())
|
||||
put(Columns.ARTIST_NAMES, rawSong.artistNames.toSQLMultiValue())
|
||||
put(
|
||||
Columns.ARTIST_SORT_NAMES,
|
||||
rawSong.artistSortNames.toSQLMultiValue())
|
||||
put(Columns.ARTIST_SORT_NAMES, rawSong.artistSortNames.toSQLMultiValue())
|
||||
|
||||
put(
|
||||
Columns.ALBUM_ARTIST_MUSIC_BRAINZ_IDS,
|
||||
rawSong.albumArtistMusicBrainzIds.toSQLMultiValue())
|
||||
put(
|
||||
Columns.ALBUM_ARTIST_NAMES,
|
||||
rawSong.albumArtistNames.toSQLMultiValue())
|
||||
put(
|
||||
Columns.ALBUM_ARTIST_SORT_NAMES,
|
||||
rawSong.albumArtistSortNames.toSQLMultiValue())
|
||||
put(Columns.ALBUM_ARTIST_NAMES, rawSong.albumArtistNames.toSQLMultiValue())
|
||||
put(Columns.ALBUM_ARTIST_SORT_NAMES, rawSong.albumArtistSortNames.toSQLMultiValue())
|
||||
|
||||
put(Columns.GENRE_NAMES, rawSong.genreNames.toSQLMultiValue())
|
||||
}
|
||||
|
|
|
@ -27,9 +27,8 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
* a [ViewModel] that manages the current music picker state.
|
||||
* Make it so that the dialogs just contain the music themselves and then exit if the library
|
||||
* changes.
|
||||
* a [ViewModel] that manages the current music picker state. Make it so that the dialogs just
|
||||
* contain the music themselves and then exit if the library changes.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class PickerViewModel : ViewModel(), MusicStore.Callback {
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.oxycblt.auxio.playback
|
|||
import org.oxycblt.auxio.IntegerTable
|
||||
|
||||
/**
|
||||
* Represents a configuration option for what kind of "secondary" action to show in a particular
|
||||
* Represents a configuration option for what kind of "secondary" action to show in a particular UI
|
||||
* context.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
|
|
|
@ -47,7 +47,10 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
|||
* available controls.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class PlaybackPanelFragment : ViewBindingFragment<FragmentPlaybackPanelBinding>(), Toolbar.OnMenuItemClickListener, StyledSeekBar.Listener {
|
||||
class PlaybackPanelFragment :
|
||||
ViewBindingFragment<FragmentPlaybackPanelBinding>(),
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
StyledSeekBar.Listener {
|
||||
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
// AudioEffect expects you to use startActivityForResult with the panel intent. There is no
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.oxycblt.auxio.playback
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
* Convert milliseconds into deci-seconds (1/10th of a second).
|
||||
|
@ -58,7 +57,7 @@ fun Long.secsToMs() = times(1000)
|
|||
fun Long.formatDurationMs(isElapsed: Boolean) = msToSecs().formatDurationSecs(isElapsed)
|
||||
|
||||
/**
|
||||
// * Format a deci-second value (1/10th of a second) into a string duration.
|
||||
* // * Format a deci-second value (1/10th of a second) into a string duration.
|
||||
* @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:--
|
||||
* will be returned if the second value is 0.
|
||||
*/
|
||||
|
|
|
@ -152,8 +152,8 @@ class PlaybackViewModel(application: Application) :
|
|||
/**
|
||||
* Play a [Song] from one of it's [Artist]s.
|
||||
* @param song The [Song] to play.
|
||||
* @param artist The [Artist] to play from. Must be linked to the [Song]. If null, the user
|
||||
* will be prompted on what artist to play. Defaults to null.
|
||||
* @param artist The [Artist] to play from. Must be linked to the [Song]. If null, the user will
|
||||
* be prompted on what artist to play. Defaults to null.
|
||||
*/
|
||||
fun playFromArtist(song: Song, artist: Artist? = null) {
|
||||
if (artist != null) {
|
||||
|
@ -234,8 +234,8 @@ class PlaybackViewModel(application: Application) :
|
|||
}
|
||||
|
||||
/**
|
||||
* Start the given [InternalPlayer.Action] to be completed eventually. This can be used
|
||||
* to enqueue a playback action at startup to then occur when the music library is fully loaded.
|
||||
* Start the given [InternalPlayer.Action] to be completed eventually. This can be used to
|
||||
* enqueue a playback action at startup to then occur when the music library is fully loaded.
|
||||
* @param action The [InternalPlayer.Action] to perform eventually.
|
||||
*/
|
||||
fun startAction(action: InternalPlayer.Action) {
|
||||
|
|
|
@ -53,7 +53,4 @@ enum class ReplayGainMode {
|
|||
* @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
|
||||
)
|
||||
data class ReplayGainPreAmp(val with: Float, val without: Float)
|
||||
|
|
|
@ -57,8 +57,8 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
|||
|
||||
/**
|
||||
* Updates the volume adjustment based on the given [Metadata].
|
||||
* @param metadata The [Metadata] of the currently playing track, or null if the track
|
||||
* has no [Metadata].
|
||||
* @param metadata The [Metadata] of the currently playing track, or null if the track has no
|
||||
* [Metadata].
|
||||
*/
|
||||
fun applyReplayGain(metadata: Metadata?) {
|
||||
// TODO: Allow this to automatically obtain it's own [Metadata].
|
||||
|
@ -155,7 +155,8 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
|||
// Grok a float from a ReplayGain tag by removing everything that is not 0-9, ,
|
||||
// or -.
|
||||
// Derived from vanilla music: https://github.com/vanilla-music/vanilla
|
||||
val gainValue = try {
|
||||
val gainValue =
|
||||
try {
|
||||
value.replace(Regex("[^\\d.-]"), "").toFloat()
|
||||
} catch (e: Exception) {
|
||||
0f
|
||||
|
@ -275,8 +276,7 @@ class ReplayGainAudioProcessor(context: Context) : BaseAudioProcessor() {
|
|||
/**
|
||||
* A raw ReplayGain adjustment.
|
||||
* @param key The tag's key.
|
||||
* @param value The tag's adjustment, in dB.
|
||||
* TODO: Try to phasse this out.
|
||||
* @param value The tag's adjustment, in dB. TODO: Try to phasse this out.
|
||||
*/
|
||||
private data class GainTag(val key: String, val value: Float)
|
||||
|
||||
|
|
|
@ -50,8 +50,8 @@ interface InternalPlayer {
|
|||
|
||||
/**
|
||||
* Get a [State] corresponding to the current player state.
|
||||
* @param durationMs The duration of the currently playing track, in milliseconds.
|
||||
* Required since the internal player cannot obtain an accurate duration itself.
|
||||
* @param durationMs The duration of the currently playing track, in milliseconds. Required
|
||||
* since the internal player cannot obtain an accurate duration itself.
|
||||
*/
|
||||
fun getState(durationMs: Long): State
|
||||
|
||||
|
@ -67,16 +67,14 @@ interface InternalPlayer {
|
|||
*/
|
||||
fun setPlaying(isPlaying: Boolean)
|
||||
|
||||
/**
|
||||
* Possible long-running background tasks handled by the background playback task.
|
||||
*/
|
||||
/** Possible long-running background tasks handled by the background playback task. */
|
||||
sealed class Action {
|
||||
/** Restore the previously saved playback state. */
|
||||
object RestoreState : Action()
|
||||
|
||||
/**
|
||||
* Start shuffled playback of the entire music library.
|
||||
* Analogous to the "Shuffle All" shortcut.
|
||||
* Start shuffled playback of the entire music library. Analogous to the "Shuffle All"
|
||||
* shortcut.
|
||||
*/
|
||||
object ShuffleAll : Action()
|
||||
|
||||
|
@ -100,8 +98,8 @@ interface InternalPlayer {
|
|||
) {
|
||||
/**
|
||||
* Calculate the "real" playback position this instance contains, in milliseconds.
|
||||
* @return If paused, the original position will be returned. Otherwise, it will be
|
||||
* the original position plus the time elapsed since this state was created.
|
||||
* @return If paused, the original position will be returned. Otherwise, it will be the
|
||||
* original position plus the time elapsed since this state was created.
|
||||
*/
|
||||
fun calculateElapsedPositionMs() =
|
||||
if (isAdvancing) {
|
||||
|
@ -154,8 +152,8 @@ interface InternalPlayer {
|
|||
companion object {
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param isPlaying Whether the player is actively playing audio or set to play audio
|
||||
* in the future.
|
||||
* @param isPlaying Whether the player is actively playing audio or set to play audio in
|
||||
* the future.
|
||||
* @param isAdvancing Whether the player is actively playing audio in this moment.
|
||||
* @param positionMs The current position of the player.
|
||||
*/
|
||||
|
|
|
@ -216,8 +216,8 @@ class PlaybackStateDatabase private constructor(context: Context) :
|
|||
)
|
||||
|
||||
/**
|
||||
* A lower-level form of [SavedState] that contains additional information to create
|
||||
* a more reliable restoration process.
|
||||
* A lower-level form of [SavedState] that contains additional information to create a more
|
||||
* reliable restoration process.
|
||||
*/
|
||||
private data class RawState(
|
||||
/** @see SavedState.index */
|
||||
|
@ -229,9 +229,8 @@ class PlaybackStateDatabase private constructor(context: Context) :
|
|||
/** @see SavedState.isShuffled */
|
||||
val isShuffled: Boolean,
|
||||
/**
|
||||
* The [Music.UID] of the [Song] that was originally in the queue at [index].
|
||||
* This can be used to restore the currently playing item in the queue if
|
||||
* the index mapping changed.
|
||||
* The [Music.UID] of the [Song] that was originally in the queue at [index]. This can be
|
||||
* used to restore the currently playing item in the queue if the index mapping changed.
|
||||
*/
|
||||
val songUid: Music.UID,
|
||||
/** @see SavedState.parent */
|
||||
|
|
|
@ -57,6 +57,7 @@ class PlaybackStateManager private constructor() {
|
|||
private val callbacks = mutableListOf<Callback>()
|
||||
private var internalPlayer: InternalPlayer? = null
|
||||
private var pendingAction: InternalPlayer.Action? = null
|
||||
private var isInitialized = false
|
||||
|
||||
/** The currently playing [Song]. Null if nothing is playing. */
|
||||
val song
|
||||
|
@ -84,9 +85,6 @@ class PlaybackStateManager private constructor() {
|
|||
/** Whether the queue is shuffled. */
|
||||
var isShuffled = false
|
||||
private set
|
||||
/** Whether this instance has played something. */
|
||||
var isInitialized = false
|
||||
private set
|
||||
/**
|
||||
* The current audio session ID of the internal player. Null if no [InternalPlayer] is
|
||||
* available.
|
||||
|
@ -94,11 +92,9 @@ class PlaybackStateManager private constructor() {
|
|||
val currentAudioSessionId: Int?
|
||||
get() = internalPlayer?.audioSessionId
|
||||
|
||||
|
||||
/**
|
||||
* Add a [Callback] to this instance. This can be used to receive changes in the playback
|
||||
* state. Will immediately invoke [Callback] methods to initialize the instance with the
|
||||
* current state.
|
||||
* Add a [Callback] to this instance. This can be used to receive changes in the playback state.
|
||||
* Will immediately invoke [Callback] methods to initialize the instance with the current state.
|
||||
* @param callback The [Callback] to add.
|
||||
* @see Callback
|
||||
*/
|
||||
|
@ -129,7 +125,8 @@ class PlaybackStateManager private constructor() {
|
|||
* Register an [InternalPlayer] for this instance. This instance will handle translating the
|
||||
* current playback state into audio playback. There can be only one [InternalPlayer] at a time.
|
||||
* Will invoke [InternalPlayer] methods to initialize the instance with the current state.
|
||||
* @param internalPlayer The [InternalPlayer] to register. Will do nothing if already registered.
|
||||
* @param internalPlayer The [InternalPlayer] to register. Will do nothing if already
|
||||
* registered.
|
||||
*/
|
||||
@Synchronized
|
||||
fun registerInternalPlayer(internalPlayer: InternalPlayer) {
|
||||
|
@ -201,8 +198,8 @@ class PlaybackStateManager private constructor() {
|
|||
// --- QUEUE FUNCTIONS ---
|
||||
|
||||
/**
|
||||
* Go to the next [Song] in the queue. Will go to the first [Song] in the queue if there
|
||||
* is no [Song] ahead to skip to.
|
||||
* Go to the next [Song] in the queue. Will go to the first [Song] in the queue if there is no
|
||||
* [Song] ahead to skip to.
|
||||
*/
|
||||
@Synchronized
|
||||
fun next() {
|
||||
|
@ -217,8 +214,8 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Go to the previous [Song] in the queue. Will rewind if there are no previous [Song]s
|
||||
* to skip to, or if configured to do so.
|
||||
* Go to the previous [Song] in the queue. Will rewind if there are no previous [Song]s to skip
|
||||
* to, or if configured to do so.
|
||||
*/
|
||||
@Synchronized
|
||||
fun prev() {
|
||||
|
@ -433,9 +430,7 @@ class PlaybackStateManager private constructor() {
|
|||
internalPlayer?.seekTo(positionMs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind to the beginning of the currently playing [Song].
|
||||
*/
|
||||
/** Rewind to the beginning of the currently playing [Song]. */
|
||||
fun rewind() = seekTo(0)
|
||||
|
||||
// --- PERSISTENCE FUNCTIONS ---
|
||||
|
@ -501,14 +496,16 @@ class PlaybackStateManager private constructor() {
|
|||
logD("Saving state to DB")
|
||||
|
||||
// Create the saved state from the current playback state.
|
||||
val state = synchronized(this) {
|
||||
val state =
|
||||
synchronized(this) {
|
||||
PlaybackStateDatabase.SavedState(
|
||||
index = index,
|
||||
parent = parent,
|
||||
queue = _queue,
|
||||
positionMs = playerState.calculateElapsedPositionMs(),
|
||||
isShuffled = isShuffled,
|
||||
repeatMode = repeatMode) }
|
||||
repeatMode = repeatMode)
|
||||
}
|
||||
return try {
|
||||
withContext(Dispatchers.IO) { database.write(state) }
|
||||
true
|
||||
|
@ -636,8 +633,8 @@ class PlaybackStateManager private constructor() {
|
|||
*/
|
||||
interface Callback {
|
||||
/**
|
||||
* Called when the position of the currently playing item has changed, changing the
|
||||
* current [Song], but no other queue attribute has changed.
|
||||
* Called when the position of the currently playing item has changed, changing the current
|
||||
* [Song], but no other queue attribute has changed.
|
||||
* @param index The new position in the queue.
|
||||
*/
|
||||
fun onIndexMoved(index: Int) {}
|
||||
|
@ -649,8 +646,8 @@ class PlaybackStateManager private constructor() {
|
|||
fun onQueueChanged(queue: List<Song>) {}
|
||||
|
||||
/**
|
||||
* Called when the queue has changed in a non-trivial manner (such as re-shuffling),
|
||||
* but the currently playing [Song] has not.
|
||||
* Called when the queue has changed in a non-trivial manner (such as re-shuffling), but the
|
||||
* currently playing [Song] has not.
|
||||
* @param index The new position in the queue.
|
||||
*/
|
||||
fun onQueueReworked(index: Int, queue: List<Song>) {}
|
||||
|
@ -659,8 +656,7 @@ class PlaybackStateManager private constructor() {
|
|||
* Called when a new playback configuration was created.
|
||||
* @param index The new position in the queue.
|
||||
* @param queue The new queue.
|
||||
* @param parent The new [MusicParent] being played from, or null if playing from all
|
||||
* songs.
|
||||
* @param parent The new [MusicParent] being played from, or null if playing from all songs.
|
||||
*/
|
||||
fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {}
|
||||
|
||||
|
@ -677,8 +673,8 @@ class PlaybackStateManager private constructor() {
|
|||
fun onRepeatChanged(repeatMode: RepeatMode) {}
|
||||
|
||||
/**
|
||||
* Called when the queue's shuffle state changes. Handling the queue change itself
|
||||
* should occur in [onQueueReworked],
|
||||
* Called when the queue's shuffle state changes. Handling the queue change itself should
|
||||
* occur in [onQueueReworked],
|
||||
* @param isShuffled Whether the queue is shuffled.
|
||||
*/
|
||||
fun onShuffledChanged(isShuffled: Boolean) {}
|
||||
|
|
|
@ -31,8 +31,8 @@ enum class RepeatMode {
|
|||
NONE,
|
||||
|
||||
/**
|
||||
* Repeat the whole queue. Songs are played immediately, and playback continues when the
|
||||
* queue repeats.
|
||||
* Repeat the whole queue. Songs are played immediately, and playback continues when the queue
|
||||
* repeats.
|
||||
*/
|
||||
ALL,
|
||||
|
||||
|
|
|
@ -74,8 +74,8 @@ class MediaSessionComponent(private val context: Context, private val callback:
|
|||
}
|
||||
|
||||
/**
|
||||
* Release this instance, closing the [MediaSessionCompat] and preventing any
|
||||
* further updates to the [NotificationComponent].
|
||||
* Release this instance, closing the [MediaSessionCompat] and preventing any further updates to
|
||||
* the [NotificationComponent].
|
||||
*/
|
||||
fun release() {
|
||||
provider.release()
|
||||
|
@ -246,10 +246,10 @@ class MediaSessionComponent(private val context: Context, private val callback:
|
|||
/**
|
||||
* Upload a new [MediaMetadataCompat] based on the current playback state to the
|
||||
* [MediaSessionCompat] and [NotificationComponent].
|
||||
* @param song The current [Song] to create the [MediaMetadataCompat] from, or null if no
|
||||
* [Song] is currently playing.
|
||||
* @param parent The current [MusicParent] to create the [MediaMetadataCompat] from, or null
|
||||
* if playback is currently occuring from all songs.
|
||||
* @param song The current [Song] to create the [MediaMetadataCompat] from, or null if no [Song]
|
||||
* is currently playing.
|
||||
* @param parent The current [MusicParent] to create the [MediaMetadataCompat] from, or null if
|
||||
* playback is currently occuring from all songs.
|
||||
*/
|
||||
private fun updateMediaMetadata(song: Song?, parent: MusicParent?) {
|
||||
if (song == null) {
|
||||
|
@ -343,7 +343,8 @@ class MediaSessionComponent(private val context: Context, private val callback:
|
|||
|
||||
val state =
|
||||
// InternalPlayer.State handles position/state information.
|
||||
playbackManager.playerState.intoPlaybackState(PlaybackStateCompat.Builder())
|
||||
playbackManager.playerState
|
||||
.intoPlaybackState(PlaybackStateCompat.Builder())
|
||||
.setActions(ACTIONS)
|
||||
// Active queue ID corresponds to the indices we populated prior, use them here.
|
||||
.setActiveQueueItemId(playbackManager.index.toLong())
|
||||
|
@ -396,9 +397,7 @@ class MediaSessionComponent(private val context: Context, private val callback:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for handling changes in the notification configuration.
|
||||
*/
|
||||
/** An interface for handling changes in the notification configuration. */
|
||||
interface Callback {
|
||||
/**
|
||||
* Called when the [NotificationComponent] changes, requiring it to be re-posed.
|
||||
|
|
|
@ -34,9 +34,8 @@ import org.oxycblt.auxio.util.newBroadcastPendingIntent
|
|||
import org.oxycblt.auxio.util.newMainPendingIntent
|
||||
|
||||
/**
|
||||
* The playback notification component. Due to race conditions regarding notification
|
||||
* updates, this component is not self-sufficient. [MediaSessionComponent] should be used
|
||||
* instead of manage it.
|
||||
* The playback notification component. Due to race conditions regarding notification updates, this
|
||||
* component is not self-sufficient. [MediaSessionComponent] should be used instead of manage it.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
|
@ -115,7 +114,8 @@ class NotificationComponent(private val context: Context, sessionToken: MediaSes
|
|||
context: Context,
|
||||
isPlaying: Boolean
|
||||
): NotificationCompat.Action {
|
||||
val drawableRes = if (isPlaying) {
|
||||
val drawableRes =
|
||||
if (isPlaying) {
|
||||
R.drawable.ic_pause_24
|
||||
} else {
|
||||
R.drawable.ic_play_24
|
||||
|
@ -134,7 +134,8 @@ class NotificationComponent(private val context: Context, sessionToken: MediaSes
|
|||
context: Context,
|
||||
isShuffled: Boolean
|
||||
): NotificationCompat.Action {
|
||||
val drawableRes = if (isShuffled) {
|
||||
val drawableRes =
|
||||
if (isShuffled) {
|
||||
R.drawable.ic_shuffle_on_24
|
||||
} else {
|
||||
R.drawable.ic_shuffle_off_24
|
||||
|
@ -142,13 +143,10 @@ class NotificationComponent(private val context: Context, sessionToken: MediaSes
|
|||
return buildAction(context, PlaybackService.ACTION_INVERT_SHUFFLE, drawableRes)
|
||||
}
|
||||
|
||||
private fun buildAction(
|
||||
context: Context,
|
||||
actionName: String,
|
||||
@DrawableRes iconRes: Int
|
||||
) =
|
||||
private fun buildAction(context: Context, actionName: String, @DrawableRes iconRes: Int) =
|
||||
NotificationCompat.Action.Builder(
|
||||
iconRes, actionName, context.newBroadcastPendingIntent(actionName)).build()
|
||||
iconRes, actionName, context.newBroadcastPendingIntent(actionName))
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
/** Notification channel used by solely the playback notification. */
|
||||
|
|
|
@ -141,7 +141,8 @@ class PlaybackService :
|
|||
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||
.build(),
|
||||
true)
|
||||
.build().also { it.addListener(this) }
|
||||
.build()
|
||||
.also { it.addListener(this) }
|
||||
// Initialize the core service components
|
||||
settings = Settings(this, this)
|
||||
foregroundManager = ForegroundManager(this)
|
||||
|
@ -163,8 +164,7 @@ class PlaybackService :
|
|||
addAction(ACTION_SKIP_NEXT)
|
||||
addAction(ACTION_EXIT)
|
||||
addAction(WidgetProvider.ACTION_WIDGET_UPDATE)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
logD("Service created")
|
||||
}
|
||||
|
@ -218,7 +218,8 @@ class PlaybackService :
|
|||
|
||||
override fun getState(durationMs: Long) =
|
||||
InternalPlayer.State.new(
|
||||
player.playWhenReady, player.isPlaying,
|
||||
player.playWhenReady,
|
||||
player.isPlaying,
|
||||
// The position value can be below zero or past the expected duration, make
|
||||
// sure we handle that.
|
||||
player.currentPosition.coerceAtLeast(0).coerceAtMost(durationMs))
|
||||
|
@ -363,7 +364,8 @@ class PlaybackService :
|
|||
}
|
||||
|
||||
override fun performAction(action: InternalPlayer.Action): Boolean {
|
||||
val library = musicStore.library
|
||||
val library =
|
||||
musicStore.library
|
||||
// No library, cannot do anything.
|
||||
?: return false
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ import android.widget.FrameLayout
|
|||
/**
|
||||
* A [FrameLayout] that programmatically overrides the child layout to a left-to-right (LTR) layout
|
||||
* direction. This is useful for "Timeline" elements that Material Design recommends be LTR in all
|
||||
* cases. This layout can only contain one child, to prevent conflicts with other layout
|
||||
* components.
|
||||
* cases. This layout can only contain one child, to prevent conflicts with other layout components.
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
open class ForcedLTRFrameLayout
|
||||
|
|
|
@ -110,8 +110,8 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
|||
/** A listener for SeekBar interactions. */
|
||||
interface Listener {
|
||||
/**
|
||||
* Called when the internal [Slider] was scrubbed to a new position, requesting that
|
||||
* a seek be performed.
|
||||
* Called when the internal [Slider] was scrubbed to a new position, requesting that a seek
|
||||
* be performed.
|
||||
* @param positionDs The position to seek to, in deci-seconds (1/10th of a second).
|
||||
*/
|
||||
fun onSeekConfirmed(positionDs: Long)
|
||||
|
|
|
@ -73,7 +73,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
|||
* @param recycler [RecyclerView] to expand with, or null if one is currently unavailable.
|
||||
*/
|
||||
fun expandWithRecycler(recycler: RecyclerView?) {
|
||||
// TODO: Is it possible to use liftOnScrollTargetViewId to avoid the [RecyclerView] argument?
|
||||
// TODO: Is it possible to use liftOnScrollTargetViewId to avoid the [RecyclerView]
|
||||
// argument?
|
||||
setExpanded(true)
|
||||
recycler?.let { addOnOffsetChangedListener(ExpansionHackListener(it)) }
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.util
|
||||
|
||||
import android.content.ContentValues
|
||||
|
@ -5,7 +22,6 @@ import android.database.Cursor
|
|||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.core.database.sqlite.transaction
|
||||
|
||||
|
||||
/**
|
||||
* Query all columns in the given [SQLiteDatabase] table, running the block when the [Cursor] is
|
||||
* loaded. The block will be called with [use], allowing for automatic cleanup of [Cursor]
|
||||
|
@ -22,22 +38,23 @@ inline fun <R> SQLiteDatabase.queryAll(tableName: String, block: (Cursor) -> R)
|
|||
* @param schema A block that adds a comma-separated list of SQL column declarations.
|
||||
*/
|
||||
inline fun SQLiteDatabase.createTable(name: String, schema: StringBuilder.() -> StringBuilder) {
|
||||
val command = StringBuilder()
|
||||
.append("CREATE TABLE IF NOT EXISTS $name(")
|
||||
.schema()
|
||||
.append(")")
|
||||
val command = StringBuilder().append("CREATE TABLE IF NOT EXISTS $name(").schema().append(")")
|
||||
execSQL(command.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely write a list of items to an [SQLiteDatabase]. This will clear the prior list and write
|
||||
* as much of the new list as possible.
|
||||
* Safely write a list of items to an [SQLiteDatabase]. This will clear the prior list and write as
|
||||
* much of the new list as possible.
|
||||
* @param list The list of items to write.
|
||||
* @param tableName The name of the table to write the items to.
|
||||
* @param transform Code to transform an item into a corresponding [ContentValues] to the given
|
||||
* table.
|
||||
*/
|
||||
inline fun <reified T> SQLiteDatabase.writeList(list: List<T>, tableName: String, transform: (Int, T) -> ContentValues) {
|
||||
inline fun <reified T> SQLiteDatabase.writeList(
|
||||
list: List<T>,
|
||||
tableName: String,
|
||||
transform: (Int, T) -> ContentValues
|
||||
) {
|
||||
// Clear any prior items in the table.
|
||||
transaction { delete(tableName, null, null) }
|
||||
|
||||
|
@ -50,14 +67,15 @@ inline fun <reified T> SQLiteDatabase.writeList(list: List<T>, tableName: String
|
|||
transaction {
|
||||
while (i < list.size) {
|
||||
val values = transform(i, list[i])
|
||||
// Increment forward now so that if this insert fails, the transactionPosition
|
||||
// Increment forward now so that if this insert fails, the transaction position
|
||||
// will still start at the next i.
|
||||
i++
|
||||
insert(tableName, null, values)
|
||||
}
|
||||
}
|
||||
transactionPosition = i
|
||||
logD("Wrote batch of ${T::class.simpleName} instances. " +
|
||||
logD(
|
||||
"Wrote batch of ${T::class.simpleName} instances. " +
|
||||
"Position is now at $transactionPosition")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
|
||||
package org.oxycblt.auxio.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.graphics.PointF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
|
@ -30,7 +27,6 @@ import androidx.activity.viewModels
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
|
|
|
@ -65,7 +65,7 @@ fun lazyReflectedField(clazz: KClass<*>, field: String) = lazy {
|
|||
* 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 field The name of the method to obtain.
|
||||
* @param method The name of the method to obtain.
|
||||
*/
|
||||
fun lazyReflectedMethod(clazz: KClass<*>, method: String) = lazy {
|
||||
clazz.java.getDeclaredMethod(method).also { it.isAccessible = true }
|
||||
|
|
|
@ -217,9 +217,9 @@ class WidgetProvider : AppWidgetProvider() {
|
|||
// widgets.
|
||||
val background =
|
||||
if (Settings(context).roundMode && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
R.drawable.ui_widget_bar_round
|
||||
R.drawable.ui_widget_bg_round
|
||||
} else {
|
||||
R.drawable.ui_widget_bar_system
|
||||
R.drawable.ui_widget_bg_system
|
||||
}
|
||||
setBackgroundResource(android.R.id.background, background)
|
||||
return this
|
||||
|
|
|
@ -80,7 +80,7 @@ fun RemoteViews.setLayoutDirection(@IdRes viewId: Int, layoutDirection: Int) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Update the app widget layouts corresponding to the given [AppWidgetProvider] [ComponentName] with
|
||||
* Update the app widget layouts corresponding to the given [WidgetProvider] [ComponentName] with
|
||||
* an adaptive layout, in a version-compatible manner.
|
||||
* @param context [Context] required to backport adaptive layout behavior.
|
||||
* @param component [ComponentName] of the app widget layout to update.
|
||||
|
|
|
@ -129,12 +129,10 @@
|
|||
<string name="fmt_lib_song_count">Canciones cargadas: %d</string>
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="one">%d canción</item>
|
||||
<item quantity="many">%d canciones</item>
|
||||
<item quantity="other">%d canciones</item>
|
||||
</plurals>
|
||||
<plurals name="fmt_album_count">
|
||||
<item quantity="one">%d álbum</item>
|
||||
<item quantity="many">%d álbumes</item>
|
||||
<item quantity="other">%d álbumes</item>
|
||||
</plurals>
|
||||
<string name="lbl_size">Tamaño</string>
|
||||
|
|
|
@ -136,12 +136,10 @@
|
|||
<string name="fmt_lib_total_duration">Durata totale: %s</string>
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="one">%d canzone</item>
|
||||
<item quantity="many">%d canzoni</item>
|
||||
<item quantity="other">%d canzoni</item>
|
||||
</plurals>
|
||||
<plurals name="fmt_album_count">
|
||||
<item quantity="one">%d disco</item>
|
||||
<item quantity="many">%d dischi</item>
|
||||
<item quantity="other">%d dischi</item>
|
||||
</plurals>
|
||||
<string name="set_dirs_mode">Modo</string>
|
||||
|
@ -252,7 +250,6 @@
|
|||
<string name="set_separators_plus">Più (+)</string>
|
||||
<plurals name="fmt_artist_count">
|
||||
<item quantity="one">%d artista</item>
|
||||
<item quantity="many">%d artisti</item>
|
||||
<item quantity="other">%d artisti</item>
|
||||
</plurals>
|
||||
<string name="set_rescan">Riscansiona musica</string>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources />
|
|
@ -63,12 +63,10 @@
|
|||
<string name="fmt_lib_song_count">Músicas carregadas: %d</string>
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="one">%d música</item>
|
||||
<item quantity="many">%d músicas</item>
|
||||
<item quantity="other">%d músicas</item>
|
||||
</plurals>
|
||||
<plurals name="fmt_album_count">
|
||||
<item quantity="one">%d álbum</item>
|
||||
<item quantity="many">%d álbuns</item>
|
||||
<item quantity="other">%d álbuns</item>
|
||||
</plurals>
|
||||
<string name="lbl_sort_asc">Crescente</string>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<style name="Widget.Auxio.AppBarLayout" parent="Widget.Material3.AppBarLayout">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<!-- Resolve lifted state flickering when scrolling fast. -->
|
||||
<item name="android:stateListAnimator">@null</item>
|
||||
</style>
|
||||
|
||||
|
|
Loading…
Reference in a new issue