all: migrate to centralized constant table
Migrate to a centralized constant table for easier management. Previously, Auxio would tie constants to the class itself, which led to a largely disjointed system that relied on an internal table so that it would stay sane. This commit moves all of those constants to a single table for easier usage and management.
This commit is contained in:
parent
6d9da35de0
commit
608112a7ac
23 changed files with 293 additions and 282 deletions
|
@ -36,9 +36,6 @@ import org.oxycblt.auxio.settings.SettingsManager
|
|||
* - Rework RecyclerView management and item dragging
|
||||
* - Rework sealed classes to minimize whens and maximize overrides
|
||||
* ```
|
||||
*
|
||||
* TODO: Dumpster int-codes for a 4-byte identifier (can still be in the form of an int) For
|
||||
* example, instead of 0xA111 for ReplayGainMode.TRACK, you would instead have RTCK
|
||||
*/
|
||||
@Suppress("UNUSED")
|
||||
class AuxioApp : Application(), ImageLoaderFactory {
|
||||
|
|
101
app/src/main/java/org/oxycblt/auxio/IntegerTable.kt
Normal file
101
app/src/main/java/org/oxycblt/auxio/IntegerTable.kt
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
object IntegerTable {
|
||||
/** SongViewHolder */
|
||||
const val ITEM_TYPE_SONG = 0xA000
|
||||
/** AlbumViewHolder */
|
||||
const val ITEM_TYPE_ALBUM = 0xA001
|
||||
/** ArtistViewHolder */
|
||||
const val ITEM_TYPE_ARTIST = 0xA002
|
||||
/** GenreViewHolder */
|
||||
const val ITEM_TYPE_GENRE = 0xA003
|
||||
/** HeaderViewHolder */
|
||||
const val ITEM_TYPE_HEADER = 0xA004
|
||||
/** ActionHeaderViewHolder */
|
||||
const val ITEM_TYPE_ACTION_HEADER = 0xA005
|
||||
|
||||
/** AlbumDetailViewHolder */
|
||||
const val ITEM_TYPE_ALBUM_DETAIL = 0xA006
|
||||
/** AlbumSongViewHolder */
|
||||
const val ITEM_TYPE_ALBUM_SONG = 0xA007
|
||||
/** ArtistDetailViewHolder */
|
||||
const val ITEM_TYPE_ARTIST_DETAIL = 0xA008
|
||||
/** ArtistAlbumViewHolder */
|
||||
const val ITEM_TYPE_ARTIST_ALBUM = 0xA009
|
||||
/** ArtistSongViewHolder */
|
||||
const val ITEM_TYPE_ARTIST_SONG = 0xA00A
|
||||
/** GenreDetailViewHolder */
|
||||
const val ITEM_TYPE_GENRE_DETAIL = 0xA00B
|
||||
/** GenreSongViewHolder */
|
||||
const val ITEM_TYPE_GENRE_SONG = 0xA00C
|
||||
|
||||
/** QueueSongViewHolder */
|
||||
const val ITEM_TYPE_QUEUE_SONG = 0xA00D
|
||||
|
||||
/** "Music playback" Notification channel */
|
||||
const val NOTIFICATION_CODE = 0xA0A0
|
||||
/** Intent request code */
|
||||
const val REQUEST_CODE = 0xA0C0
|
||||
|
||||
/** LoopMode.NONE */
|
||||
const val LOOP_MODE_NONE = 0xA100
|
||||
/** LoopMode.ALL */
|
||||
const val LOOP_MODE_ALL = 0xA101
|
||||
/** LoopMode.TRACK */
|
||||
const val LOOP_MODE_TRACK = 0xA102
|
||||
|
||||
/** PlaybackMode.IN_GENRE */
|
||||
const val PLAYBACK_MODE_IN_GENRE = 0xA103
|
||||
/** PlaybackMode.IN_ARTIST */
|
||||
const val PLAYBACK_MODE_IN_ARTIST = 0xA104
|
||||
/** PlaybackMode.IN_ALBUM */
|
||||
const val PLAYBACK_MODE_IN_ALBUM = 0xA105
|
||||
/** PlaybackMode.ALL_SONGS */
|
||||
const val PLAYBACK_MODE_ALL_SONGS = 0xA106
|
||||
|
||||
/** DisplayMode.NONE (No Longer used but still reserved) */
|
||||
// const val DISPLAY_MODE_NONE = 0xA107
|
||||
/** DisplayMode.SHOW_GENRES */
|
||||
const val DISPLAY_MODE_SHOW_GENRES = 0xA108
|
||||
/** DisplayMode.SHOW_ARTISTS */
|
||||
const val DISPLAY_MODE_SHOW_ARTISTS = 0xA109
|
||||
/** DisplayMode.SHOW_ALBUMS */
|
||||
const val DISPLAY_MODE_SHOW_ALBUMS = 0xA10A
|
||||
/** DisplayMode.SHOW_SONGS */
|
||||
const val DISPLAY_MODE_SHOW_SONGS = 0xA10B
|
||||
|
||||
/** Sort.ByName */
|
||||
const val SORT_BY_NAME = 0xA10C
|
||||
/** Sort.ByArtist */
|
||||
const val SORT_BY_ARTIST = 0xA10D
|
||||
/** Sort.ByAlbum */
|
||||
const val SORT_BY_ALBUM = 0xA10E
|
||||
/** Sort.ByYear */
|
||||
const val SORT_BY_YEAR = 0xA10F
|
||||
|
||||
/** ReplayGainMode.Off */
|
||||
const val REPLAY_GAIN_MODE_OFF = 0xA110
|
||||
/** ReplayGainMode.Track */
|
||||
const val REPLAY_GAIN_MODE_TRACK = 0xA111
|
||||
/** ReplayGainMode.Album */
|
||||
const val REPLAY_GAIN_MODE_ALBUM = 0xA112
|
||||
/** ReplayGainMode.Dynamic */
|
||||
const val REPLAY_GAIN_MODE_DYNAMIC = 0xA113
|
||||
}
|
|
@ -22,6 +22,7 @@ import android.view.ViewGroup
|
|||
import androidx.core.view.isInvisible
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.coil.bindAlbumArt
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
|
@ -53,20 +54,20 @@ class AlbumDetailAdapter(
|
|||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Album -> ALBUM_DETAIL_ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> ALBUM_SONG_ITEM_TYPE
|
||||
is Album -> IntegerTable.ITEM_TYPE_ALBUM_DETAIL
|
||||
is ActionHeader -> IntegerTable.ITEM_TYPE_ACTION_HEADER
|
||||
is Song -> IntegerTable.ITEM_TYPE_ALBUM_SONG
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ALBUM_DETAIL_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ALBUM_DETAIL ->
|
||||
AlbumDetailViewHolder(ItemDetailBinding.inflate(parent.context.inflater))
|
||||
ALBUM_SONG_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ACTION_HEADER -> ActionHeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_ALBUM_SONG ->
|
||||
AlbumSongViewHolder(ItemAlbumSongBinding.inflate(parent.context.inflater))
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -172,9 +173,4 @@ class AlbumDetailAdapter(
|
|||
binding.songTrackPlaceholder.isActivated = isHighlighted
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ALBUM_DETAIL_ITEM_TYPE = 0xA006
|
||||
const val ALBUM_SONG_ITEM_TYPE = 0xA007
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.coil.bindArtistImage
|
||||
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
|
||||
|
@ -58,25 +59,25 @@ class ArtistDetailAdapter(
|
|||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Artist -> ARTIST_DETAIL_ITEM_TYPE
|
||||
is Album -> ARTIST_ALBUM_ITEM_TYPE
|
||||
is Song -> ARTIST_SONG_ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Artist -> IntegerTable.ITEM_TYPE_ARTIST_DETAIL
|
||||
is Album -> IntegerTable.ITEM_TYPE_ARTIST_ALBUM
|
||||
is Song -> IntegerTable.ITEM_TYPE_ARTIST_SONG
|
||||
is Header -> IntegerTable.ITEM_TYPE_HEADER
|
||||
is ActionHeader -> IntegerTable.ITEM_TYPE_ACTION_HEADER
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ARTIST_DETAIL_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ARTIST_DETAIL ->
|
||||
ArtistDetailViewHolder(ItemDetailBinding.inflate(parent.context.inflater))
|
||||
ARTIST_ALBUM_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ARTIST_ALBUM ->
|
||||
ArtistAlbumViewHolder(ItemArtistAlbumBinding.inflate(parent.context.inflater))
|
||||
ARTIST_SONG_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ARTIST_SONG ->
|
||||
ArtistSongViewHolder(ItemArtistSongBinding.inflate(parent.context.inflater))
|
||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_HEADER -> HeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_ACTION_HEADER -> ActionHeaderViewHolder.from(parent.context)
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -222,10 +223,4 @@ class ArtistDetailAdapter(
|
|||
binding.songName.isActivated = isHighlighted
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARTIST_DETAIL_ITEM_TYPE = 0xA008
|
||||
const val ARTIST_ALBUM_ITEM_TYPE = 0xA009
|
||||
const val ARTIST_SONG_ITEM_TYPE = 0xA00A
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.coil.bindGenreImage
|
||||
import org.oxycblt.auxio.databinding.ItemDetailBinding
|
||||
|
@ -50,22 +51,22 @@ class GenreDetailAdapter(
|
|||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Genre -> GENRE_DETAIL_ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> GENRE_SONG_ITEM_TYPE
|
||||
is Genre -> IntegerTable.ITEM_TYPE_GENRE_DETAIL
|
||||
is ActionHeader -> IntegerTable.ITEM_TYPE_ACTION_HEADER
|
||||
is Song -> IntegerTable.ITEM_TYPE_GENRE_SONG
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
GENRE_DETAIL_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_GENRE_DETAIL ->
|
||||
GenreDetailViewHolder(ItemDetailBinding.inflate(parent.context.inflater))
|
||||
GENRE_SONG_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ACTION_HEADER -> ActionHeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_GENRE_SONG ->
|
||||
GenreSongViewHolder(
|
||||
ItemGenreSongBinding.inflate(parent.context.inflater),
|
||||
)
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
else -> error("Bad ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -154,9 +155,4 @@ class GenreDetailAdapter(
|
|||
binding.songName.isActivated = isHighlighted
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val GENRE_DETAIL_ITEM_TYPE = 0xA00B
|
||||
const val GENRE_SONG_ITEM_TYPE = 0xA00C
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleRes: Int = 0)
|
|||
startAngle: Float,
|
||||
sweepAngle: Float
|
||||
) {
|
||||
path.arcTo(
|
||||
arcTo(
|
||||
centerX - radius,
|
||||
centerY - radius,
|
||||
centerX + radius,
|
||||
|
|
|
@ -27,6 +27,7 @@ import androidx.recyclerview.widget.AsyncListDiffer
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Header
|
||||
|
@ -55,19 +56,19 @@ class QueueAdapter(private val touchHelper: ItemTouchHelper) :
|
|||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (data[position]) {
|
||||
is Song -> QUEUE_SONG_ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> IntegerTable.ITEM_TYPE_QUEUE_SONG
|
||||
is Header -> IntegerTable.ITEM_TYPE_HEADER
|
||||
is ActionHeader -> IntegerTable.ITEM_TYPE_ACTION_HEADER
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
QUEUE_SONG_ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_QUEUE_SONG ->
|
||||
QueueSongViewHolder(ItemQueueSongBinding.inflate(parent.context.inflater))
|
||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_HEADER -> HeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_ACTION_HEADER -> ActionHeaderViewHolder.from(parent.context)
|
||||
else -> error("Invalid ViewHolder item type $viewType")
|
||||
}
|
||||
}
|
||||
|
@ -146,8 +147,4 @@ class QueueAdapter(private val touchHelper: ItemTouchHelper) :
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val QUEUE_SONG_ITEM_TYPE = 0xA00D
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.oxycblt.auxio.playback.state
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
|
||||
/**
|
||||
* Enum that determines the playback repeat mode.
|
||||
* @author OxygenCobalt
|
||||
|
@ -39,25 +41,21 @@ enum class LoopMode {
|
|||
* Convert the LoopMode to an int constant that is saved in PlaybackStateDatabase
|
||||
* @return The int constant for this mode
|
||||
*/
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
NONE -> INT_NONE
|
||||
ALL -> INT_ALL
|
||||
TRACK -> INT_TRACK
|
||||
}
|
||||
}
|
||||
val intCode: Int
|
||||
get() =
|
||||
when (this) {
|
||||
NONE -> IntegerTable.LOOP_MODE_NONE
|
||||
ALL -> IntegerTable.LOOP_MODE_ALL
|
||||
TRACK -> IntegerTable.LOOP_MODE_TRACK
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INT_NONE = 0xA100
|
||||
private const val INT_ALL = 0xA101
|
||||
private const val INT_TRACK = 0xA102
|
||||
|
||||
/** Convert an int [constant] into a LoopMode, or null if it isn't valid. */
|
||||
fun fromInt(constant: Int): LoopMode? {
|
||||
fun fromIntCode(constant: Int): LoopMode? {
|
||||
return when (constant) {
|
||||
INT_NONE -> NONE
|
||||
INT_ALL -> ALL
|
||||
INT_TRACK -> TRACK
|
||||
IntegerTable.LOOP_MODE_NONE -> NONE
|
||||
IntegerTable.LOOP_MODE_ALL -> ALL
|
||||
IntegerTable.LOOP_MODE_TRACK -> TRACK
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.oxycblt.auxio.playback.state
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
|
||||
/**
|
||||
* Enum that indicates how the queue should be constructed.
|
||||
* @author OxygenCobalt
|
||||
|
@ -35,32 +37,26 @@ enum class PlaybackMode {
|
|||
* Convert the mode into an int constant, to be saved in PlaybackStateDatabase
|
||||
* @return The constant for this mode,
|
||||
*/
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
ALL_SONGS -> INT_ALL_SONGS
|
||||
IN_ALBUM -> INT_IN_ALBUM
|
||||
IN_ARTIST -> INT_IN_ARTIST
|
||||
IN_GENRE -> INT_IN_GENRE
|
||||
}
|
||||
}
|
||||
val intCode: Int
|
||||
get() =
|
||||
when (this) {
|
||||
ALL_SONGS -> IntegerTable.PLAYBACK_MODE_ALL_SONGS
|
||||
IN_ALBUM -> IntegerTable.PLAYBACK_MODE_IN_ALBUM
|
||||
IN_ARTIST -> IntegerTable.PLAYBACK_MODE_IN_ARTIST
|
||||
IN_GENRE -> IntegerTable.PLAYBACK_MODE_IN_GENRE
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Kept in reverse order because of backwards compat, do not re-order these
|
||||
private const val INT_ALL_SONGS = 0xA106
|
||||
private const val INT_IN_ALBUM = 0xA105
|
||||
private const val INT_IN_ARTIST = 0xA104
|
||||
private const val INT_IN_GENRE = 0xA103
|
||||
|
||||
/**
|
||||
* Get a [PlaybackMode] for an int [constant]
|
||||
* @return The mode, null if there isn't one for this.
|
||||
*/
|
||||
fun fromInt(constant: Int): PlaybackMode? {
|
||||
return when (constant) {
|
||||
INT_ALL_SONGS -> ALL_SONGS
|
||||
INT_IN_ALBUM -> IN_ALBUM
|
||||
INT_IN_ARTIST -> IN_ARTIST
|
||||
INT_IN_GENRE -> IN_GENRE
|
||||
IntegerTable.PLAYBACK_MODE_ALL_SONGS -> ALL_SONGS
|
||||
IntegerTable.PLAYBACK_MODE_IN_ALBUM -> IN_ALBUM
|
||||
IntegerTable.PLAYBACK_MODE_IN_ARTIST -> IN_ARTIST
|
||||
IntegerTable.PLAYBACK_MODE_IN_GENRE -> IN_GENRE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.oxycblt.auxio.util.queryAll
|
|||
* A SQLite database for managing the persistent playback state and queue. Yes. I know Room exists.
|
||||
* But that would needlessly bloat my app and has crippling bugs.
|
||||
* @author OxygenCobalt
|
||||
*
|
||||
* TODO: Rework to rely on queue indices more and only use specific items as fallbacks
|
||||
*/
|
||||
class PlaybackStateDatabase(context: Context) :
|
||||
SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
|
@ -144,7 +146,7 @@ class PlaybackStateDatabase(context: Context) :
|
|||
queueIndex = cursor.getInt(indexIndex),
|
||||
playbackMode = mode,
|
||||
isShuffling = cursor.getInt(shuffleIndex) == 1,
|
||||
loopMode = LoopMode.fromInt(cursor.getInt(loopModeIndex)) ?: LoopMode.NONE,
|
||||
loopMode = LoopMode.fromIntCode(cursor.getInt(loopModeIndex)) ?: LoopMode.NONE,
|
||||
)
|
||||
|
||||
logD("Successfully read playback state: $state")
|
||||
|
@ -169,9 +171,9 @@ class PlaybackStateDatabase(context: Context) :
|
|||
put(StateColumns.COLUMN_POSITION, state.position)
|
||||
put(StateColumns.COLUMN_PARENT_HASH, state.parent?.id)
|
||||
put(StateColumns.COLUMN_QUEUE_INDEX, state.queueIndex)
|
||||
put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
|
||||
put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.intCode)
|
||||
put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
|
||||
put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
|
||||
put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.intCode)
|
||||
}
|
||||
|
||||
insert(TABLE_NAME_STATE, null, stateData)
|
||||
|
|
|
@ -49,7 +49,6 @@ private constructor(private val context: Context, mediaToken: MediaSessionCompat
|
|||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
setShowWhen(false)
|
||||
setSilent(true)
|
||||
setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
|
||||
setContentIntent(context.newMainIntent())
|
||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
||||
|
@ -159,7 +158,6 @@ private constructor(private val context: Context, mediaToken: MediaSessionCompat
|
|||
|
||||
companion object {
|
||||
const val CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel.PLAYBACK"
|
||||
const val NOTIFICATION_ID = 0xA0A0
|
||||
|
||||
/** Build a new instance of [PlaybackNotification]. */
|
||||
fun from(
|
||||
|
|
|
@ -51,6 +51,7 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
|
@ -407,18 +408,17 @@ class PlaybackService :
|
|||
// Specify that this is a media service, if supported.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(
|
||||
PlaybackNotification.NOTIFICATION_ID,
|
||||
IntegerTable.NOTIFICATION_CODE,
|
||||
notification.build(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
|
||||
} else {
|
||||
startForeground(PlaybackNotification.NOTIFICATION_ID, notification.build())
|
||||
startForeground(IntegerTable.NOTIFICATION_CODE, notification.build())
|
||||
}
|
||||
|
||||
isForeground = true
|
||||
} else {
|
||||
// If we are already in foreground just update the notification
|
||||
notificationManager.notify(
|
||||
PlaybackNotification.NOTIFICATION_ID, notification.build())
|
||||
notificationManager.notify(IntegerTable.NOTIFICATION_CODE, notification.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ class PlaybackService :
|
|||
/** Stop the foreground state and hide the notification */
|
||||
private fun stopForegroundAndNotification() {
|
||||
stopForeground(true)
|
||||
notificationManager.cancel(PlaybackNotification.NOTIFICATION_ID)
|
||||
notificationManager.cancel(IntegerTable.NOTIFICATION_CODE)
|
||||
isForeground = false
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.oxycblt.auxio.playback.system
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
|
||||
/** Represents the current setting for ReplayGain. */
|
||||
enum class ReplayGainMode {
|
||||
/** Do not apply ReplayGain. */
|
||||
|
@ -28,29 +30,13 @@ enum class ReplayGainMode {
|
|||
/** Apply the album gain only when playing from an album, defaulting to track gain otherwise. */
|
||||
DYNAMIC;
|
||||
|
||||
/** Converts this type to an integer constant. */
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
OFF -> INT_OFF
|
||||
TRACK -> INT_TRACK
|
||||
ALBUM -> INT_ALBUM
|
||||
DYNAMIC -> INT_DYNAMIC
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INT_OFF = 0xA110
|
||||
private const val INT_TRACK = 0xA111
|
||||
private const val INT_ALBUM = 0xA112
|
||||
private const val INT_DYNAMIC = 0xA113
|
||||
|
||||
/** Converts an integer constant to this type. */
|
||||
fun fromInt(value: Int): ReplayGainMode? {
|
||||
fun fromIntCode(value: Int): ReplayGainMode? {
|
||||
return when (value) {
|
||||
INT_OFF -> OFF
|
||||
INT_TRACK -> TRACK
|
||||
INT_ALBUM -> ALBUM
|
||||
INT_DYNAMIC -> DYNAMIC
|
||||
IntegerTable.REPLAY_GAIN_MODE_OFF -> OFF
|
||||
IntegerTable.REPLAY_GAIN_MODE_TRACK -> TRACK
|
||||
IntegerTable.REPLAY_GAIN_MODE_ALBUM -> ALBUM
|
||||
IntegerTable.REPLAY_GAIN_MODE_DYNAMIC -> DYNAMIC
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -46,26 +47,26 @@ class SearchAdapter(
|
|||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Genre -> GenreViewHolder.ITEM_TYPE
|
||||
is Artist -> ArtistViewHolder.ITEM_TYPE
|
||||
is Album -> AlbumViewHolder.ITEM_TYPE
|
||||
is Song -> SongViewHolder.ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is Genre -> IntegerTable.ITEM_TYPE_GENRE
|
||||
is Artist -> IntegerTable.ITEM_TYPE_ARTIST
|
||||
is Album -> IntegerTable.ITEM_TYPE_ALBUM
|
||||
is Song -> IntegerTable.ITEM_TYPE_SONG
|
||||
is Header -> IntegerTable.ITEM_TYPE_HEADER
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
GenreViewHolder.ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_GENRE ->
|
||||
GenreViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
ArtistViewHolder.ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ARTIST ->
|
||||
ArtistViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
AlbumViewHolder.ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_ALBUM ->
|
||||
AlbumViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
SongViewHolder.ITEM_TYPE ->
|
||||
IntegerTable.ITEM_TYPE_SONG ->
|
||||
SongViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||
IntegerTable.ITEM_TYPE_HEADER -> HeaderViewHolder.from(parent.context)
|
||||
else -> error("Invalid ViewHolder item type")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ class SettingsManager private constructor(context: Context) :
|
|||
/** The current ReplayGain configuration */
|
||||
val replayGainMode: ReplayGainMode
|
||||
get() =
|
||||
ReplayGainMode.fromInt(prefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
|
||||
ReplayGainMode.fromIntCode(prefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
|
||||
?: ReplayGainMode.OFF
|
||||
|
||||
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
||||
|
@ -129,50 +129,54 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The current filter mode of the search tab */
|
||||
var searchFilterMode: DisplayMode?
|
||||
get() = DisplayMode.fromFilterInt(prefs.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE))
|
||||
get() = DisplayMode.fromInt(prefs.getInt(KEY_SEARCH_FILTER_MODE, Int.MIN_VALUE))
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_SEARCH_FILTER_MODE, DisplayMode.toFilterInt(value))
|
||||
putInt(KEY_SEARCH_FILTER_MODE, value?.intCode ?: Int.MIN_VALUE)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** The song sort mode on HomeFragment */
|
||||
var libSongSort: Sort
|
||||
get() = Sort.fromInt(prefs.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
get() =
|
||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_SONGS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_SONGS_SORT, value.toInt())
|
||||
putInt(KEY_LIB_SONGS_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** The album sort mode on HomeFragment */
|
||||
var libAlbumSort: Sort
|
||||
get() = Sort.fromInt(prefs.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
get() =
|
||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_ALBUMS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_ALBUMS_SORT, value.toInt())
|
||||
putInt(KEY_LIB_ALBUMS_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** The artist sort mode on HomeFragment */
|
||||
var libArtistSort: Sort
|
||||
get() = Sort.fromInt(prefs.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
get() =
|
||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_ARTISTS_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_ARTISTS_SORT, value.toInt())
|
||||
putInt(KEY_LIB_ARTISTS_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** The genre sort mode on HomeFragment */
|
||||
var libGenreSort: Sort
|
||||
get() = Sort.fromInt(prefs.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
get() =
|
||||
Sort.fromIntCode(prefs.getInt(KEY_LIB_GENRES_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_LIB_GENRES_SORT, value.toInt())
|
||||
putInt(KEY_LIB_GENRES_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
@ -180,10 +184,11 @@ class SettingsManager private constructor(context: Context) :
|
|||
/** The detail album sort mode */
|
||||
var detailAlbumSort: Sort
|
||||
get() =
|
||||
Sort.fromInt(prefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE))
|
||||
?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_ALBUM_SORT, value.toInt())
|
||||
putInt(KEY_DETAIL_ALBUM_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
@ -191,10 +196,11 @@ class SettingsManager private constructor(context: Context) :
|
|||
/** The detail artist sort mode */
|
||||
var detailArtistSort: Sort
|
||||
get() =
|
||||
Sort.fromInt(prefs.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE)) ?: Sort.ByYear(false)
|
||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_ARTIST_SORT, Int.MIN_VALUE))
|
||||
?: Sort.ByYear(false)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_ARTIST_SORT, value.toInt())
|
||||
putInt(KEY_DETAIL_ARTIST_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
@ -202,10 +208,11 @@ class SettingsManager private constructor(context: Context) :
|
|||
/** The detail genre sort mode */
|
||||
var detailGenreSort: Sort
|
||||
get() =
|
||||
Sort.fromInt(prefs.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE)) ?: Sort.ByName(true)
|
||||
Sort.fromIntCode(prefs.getInt(KEY_DETAIL_GENRE_SORT, Int.MIN_VALUE))
|
||||
?: Sort.ByName(true)
|
||||
set(value) {
|
||||
prefs.edit {
|
||||
putInt(KEY_DETAIL_GENRE_SORT, value.toInt())
|
||||
putInt(KEY_DETAIL_GENRE_SORT, value.intCode)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.oxycblt.auxio.ui
|
||||
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
/**
|
||||
|
@ -49,39 +50,27 @@ enum class DisplayMode {
|
|||
SHOW_GENRES -> R.drawable.ic_genre
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INT_NULL = 0xA107
|
||||
private const val INT_SHOW_GENRES = 0xA108
|
||||
private const val INT_SHOW_ARTISTS = 0xA109
|
||||
private const val INT_SHOW_ALBUMS = 0xA10A
|
||||
private const val INT_SHOW_SONGS = 0xA10B
|
||||
|
||||
/**
|
||||
* Convert this enum into an integer for filtering. In this context, a null value means to
|
||||
* filter nothing.
|
||||
* @return An integer constant for that display mode, or a constant for a null [DisplayMode]
|
||||
*/
|
||||
fun toFilterInt(value: DisplayMode?): Int {
|
||||
return when (value) {
|
||||
SHOW_SONGS -> INT_SHOW_SONGS
|
||||
SHOW_ALBUMS -> INT_SHOW_ALBUMS
|
||||
SHOW_ARTISTS -> INT_SHOW_ARTISTS
|
||||
SHOW_GENRES -> INT_SHOW_GENRES
|
||||
null -> INT_NULL
|
||||
val intCode: Int
|
||||
get() =
|
||||
when (this) {
|
||||
SHOW_SONGS -> IntegerTable.DISPLAY_MODE_SHOW_SONGS
|
||||
SHOW_ALBUMS -> IntegerTable.DISPLAY_MODE_SHOW_ALBUMS
|
||||
SHOW_ARTISTS -> IntegerTable.DISPLAY_MODE_SHOW_ARTISTS
|
||||
SHOW_GENRES -> IntegerTable.DISPLAY_MODE_SHOW_GENRES
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Convert a filtering integer to a [DisplayMode]. In this context, a null value means to
|
||||
* filter nothing.
|
||||
* @return A [DisplayMode] for this constant (including null)
|
||||
*/
|
||||
fun fromFilterInt(value: Int): DisplayMode? {
|
||||
fun fromInt(value: Int): DisplayMode? {
|
||||
return when (value) {
|
||||
INT_SHOW_SONGS -> SHOW_SONGS
|
||||
INT_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
INT_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
INT_SHOW_GENRES -> SHOW_GENRES
|
||||
IntegerTable.DISPLAY_MODE_SHOW_SONGS -> SHOW_SONGS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
IntegerTable.DISPLAY_MODE_SHOW_GENRES -> SHOW_GENRES
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.oxycblt.auxio.ui
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
|
@ -43,6 +44,9 @@ import org.oxycblt.auxio.util.logW
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
sealed class Sort(open val isAscending: Boolean) {
|
||||
protected abstract val sortIntCode: Int
|
||||
abstract val itemId: Int
|
||||
|
||||
open fun songs(songs: Collection<Song>): List<Song> {
|
||||
logW("This sort is not supported for songs")
|
||||
return songs.toList()
|
||||
|
@ -63,8 +67,20 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
return genres.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply [newIsAscending] to the status of this sort.
|
||||
* @return A new [Sort] with the value of [newIsAscending] applied.
|
||||
*/
|
||||
abstract fun ascending(newIsAscending: Boolean): Sort
|
||||
|
||||
/** Sort by the names of an item */
|
||||
class ByName(override val isAscending: Boolean) : Sort(isAscending) {
|
||||
override val sortIntCode: Int
|
||||
get() = IntegerTable.SORT_BY_NAME
|
||||
|
||||
override val itemId: Int
|
||||
get() = R.id.option_sort_name
|
||||
|
||||
override fun songs(songs: Collection<Song>): List<Song> {
|
||||
return songs.sortedWith(compareByDynamic(NameComparator()) { it })
|
||||
}
|
||||
|
@ -80,10 +96,20 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
override fun genres(genres: Collection<Genre>): List<Genre> {
|
||||
return genres.sortedWith(compareByDynamic(NameComparator()) { it })
|
||||
}
|
||||
|
||||
override fun ascending(newIsAscending: Boolean): Sort {
|
||||
return ByName(isAscending)
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort by the album of an item, only supported by [Song] */
|
||||
class ByAlbum(override val isAscending: Boolean) : Sort(isAscending) {
|
||||
override val sortIntCode: Int
|
||||
get() = IntegerTable.SORT_BY_ALBUM
|
||||
|
||||
override val itemId: Int
|
||||
get() = R.id.option_sort_album
|
||||
|
||||
override fun songs(songs: Collection<Song>): List<Song> {
|
||||
return songs.sortedWith(
|
||||
MultiComparator(
|
||||
|
@ -91,10 +117,20 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
compareBy(NullableComparator()) { it.track },
|
||||
compareBy(NameComparator()) { it }))
|
||||
}
|
||||
|
||||
override fun ascending(newIsAscending: Boolean): Sort {
|
||||
return ByAlbum(newIsAscending)
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort by the artist of an item, only supported by [Album] and [Song] */
|
||||
class ByArtist(override val isAscending: Boolean) : Sort(isAscending) {
|
||||
override val sortIntCode: Int
|
||||
get() = IntegerTable.SORT_BY_ARTIST
|
||||
|
||||
override val itemId: Int
|
||||
get() = R.id.option_sort_artist
|
||||
|
||||
override fun songs(songs: Collection<Song>): List<Song> {
|
||||
return songs.sortedWith(
|
||||
MultiComparator(
|
||||
|
@ -112,10 +148,20 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
compareByDescending(NullableComparator()) { it.year },
|
||||
compareBy(NameComparator()) { it }))
|
||||
}
|
||||
|
||||
override fun ascending(newIsAscending: Boolean): Sort {
|
||||
return ByArtist(newIsAscending)
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort by the year of an item, only supported by [Album] and [Song] */
|
||||
class ByYear(override val isAscending: Boolean) : Sort(isAscending) {
|
||||
override val sortIntCode: Int
|
||||
get() = IntegerTable.SORT_BY_YEAR
|
||||
|
||||
override val itemId: Int
|
||||
get() = R.id.option_sort_year
|
||||
|
||||
override fun songs(songs: Collection<Song>): List<Song> {
|
||||
return songs.sortedWith(
|
||||
MultiComparator(
|
||||
|
@ -131,31 +177,15 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
compareByDynamic(NullableComparator()) { it.year },
|
||||
compareBy(NameComparator()) { it }))
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the corresponding item id for this sort. */
|
||||
val itemId: Int
|
||||
get() =
|
||||
when (this) {
|
||||
is ByName -> R.id.option_sort_name
|
||||
is ByArtist -> R.id.option_sort_artist
|
||||
is ByAlbum -> R.id.option_sort_album
|
||||
is ByYear -> R.id.option_sort_year
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply [ascending] to the status of this sort.
|
||||
* @return A new [Sort] with the value of [ascending] applied.
|
||||
*/
|
||||
fun ascending(ascending: Boolean): Sort {
|
||||
return when (this) {
|
||||
is ByName -> ByName(ascending)
|
||||
is ByArtist -> ByArtist(ascending)
|
||||
is ByAlbum -> ByAlbum(ascending)
|
||||
is ByYear -> ByYear(ascending)
|
||||
override fun ascending(newIsAscending: Boolean): Sort {
|
||||
return ByYear(newIsAscending)
|
||||
}
|
||||
}
|
||||
|
||||
val intCode: Int
|
||||
get() = sortIntCode.shl(1) or if (isAscending) 1 else 0
|
||||
|
||||
/**
|
||||
* Assign a new [id] to this sort
|
||||
* @return A new [Sort] corresponding to the [id] given, null if the ID has no analogue.
|
||||
|
@ -197,16 +227,6 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
return songs(genre.songs)
|
||||
}
|
||||
|
||||
/** Convert this sort to it's integer representation. */
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
is ByName -> INT_NAME
|
||||
is ByArtist -> INT_ARTIST
|
||||
is ByAlbum -> INT_ALBUM
|
||||
is ByYear -> INT_YEAR
|
||||
}.shl(1) or if (isAscending) 1 else 0
|
||||
}
|
||||
|
||||
protected inline fun <T : Music, K> compareByDynamic(
|
||||
comparator: Comparator<in K>,
|
||||
crossinline selector: (T) -> K
|
||||
|
@ -244,8 +264,8 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
*
|
||||
* Sorts often need to compare multiple things at once across several hierarchies, with this
|
||||
* class doing such in a more efficient manner than resorting at multiple intervals or grouping
|
||||
* items up. Comparators are checked from first to last, with the first comparator that returns a
|
||||
* non-equal result being propagated upwards.
|
||||
* items up. Comparators are checked from first to last, with the first comparator that returns
|
||||
* a non-equal result being propagated upwards.
|
||||
*/
|
||||
class MultiComparator<T>(vararg comparators: Comparator<T>) : Comparator<T> {
|
||||
private val mComparators = comparators
|
||||
|
@ -263,24 +283,19 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val INT_NAME = 0xA10C
|
||||
private const val INT_ARTIST = 0xA10D
|
||||
private const val INT_ALBUM = 0xA10E
|
||||
private const val INT_YEAR = 0xA10F
|
||||
|
||||
/**
|
||||
* Convert a sort's integer representation into a [Sort] instance.
|
||||
*
|
||||
* @return A [Sort] instance, null if the data is malformed.
|
||||
*/
|
||||
fun fromInt(value: Int): Sort? {
|
||||
fun fromIntCode(value: Int): Sort? {
|
||||
val ascending = (value and 1) == 1
|
||||
|
||||
return when (value.shr(1)) {
|
||||
INT_NAME -> ByName(ascending)
|
||||
INT_ARTIST -> ByArtist(ascending)
|
||||
INT_ALBUM -> ByAlbum(ascending)
|
||||
INT_YEAR -> ByYear(ascending)
|
||||
IntegerTable.SORT_BY_NAME -> ByName(ascending)
|
||||
IntegerTable.SORT_BY_ARTIST -> ByArtist(ascending)
|
||||
IntegerTable.SORT_BY_ALBUM -> ByAlbum(ascending)
|
||||
IntegerTable.SORT_BY_YEAR -> ByYear(ascending)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,8 +100,6 @@ private constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA000
|
||||
|
||||
/** Create an instance of [SongViewHolder] */
|
||||
fun from(
|
||||
context: Context,
|
||||
|
@ -128,8 +126,6 @@ private constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA001
|
||||
|
||||
/** Create an instance of [AlbumViewHolder] */
|
||||
fun from(
|
||||
context: Context,
|
||||
|
@ -156,8 +152,6 @@ private constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA002
|
||||
|
||||
/** Create an instance of [ArtistViewHolder] */
|
||||
fun from(
|
||||
context: Context,
|
||||
|
@ -184,8 +178,6 @@ private constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA003
|
||||
|
||||
/** Create an instance of [GenreViewHolder] */
|
||||
fun from(
|
||||
context: Context,
|
||||
|
@ -207,8 +199,6 @@ class HeaderViewHolder private constructor(private val binding: ItemHeaderBindin
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA004
|
||||
|
||||
/** Create an instance of [HeaderViewHolder] */
|
||||
fun from(context: Context): HeaderViewHolder {
|
||||
return HeaderViewHolder(ItemHeaderBinding.inflate(context.inflater))
|
||||
|
@ -233,8 +223,6 @@ class ActionHeaderViewHolder private constructor(private val binding: ItemAction
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA005
|
||||
|
||||
/** Create an instance of [ActionHeaderViewHolder] */
|
||||
fun from(context: Context): ActionHeaderViewHolder {
|
||||
return ActionHeaderViewHolder(ItemActionHeaderBinding.inflate(context.inflater))
|
||||
|
|
|
@ -40,10 +40,9 @@ import androidx.annotation.StringRes
|
|||
import androidx.core.content.ContextCompat
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.system.exitProcess
|
||||
import org.oxycblt.auxio.IntegerTable
|
||||
import org.oxycblt.auxio.MainActivity
|
||||
|
||||
const val INTENT_REQUEST_CODE = 0xA0A0
|
||||
|
||||
/** Shortcut to get a [LayoutInflater] from a [Context] */
|
||||
val Context.inflater: LayoutInflater
|
||||
get() = LayoutInflater.from(this)
|
||||
|
@ -213,7 +212,7 @@ fun Context.showToast(@StringRes str: Int) {
|
|||
fun Context.newMainIntent(): PendingIntent {
|
||||
return PendingIntent.getActivity(
|
||||
this,
|
||||
INTENT_REQUEST_CODE,
|
||||
IntegerTable.REQUEST_CODE,
|
||||
Intent(this, MainActivity::class.java),
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||
}
|
||||
|
@ -222,7 +221,7 @@ fun Context.newMainIntent(): PendingIntent {
|
|||
fun Context.newBroadcastIntent(what: String): PendingIntent {
|
||||
return PendingIntent.getBroadcast(
|
||||
this,
|
||||
INTENT_REQUEST_CODE,
|
||||
IntegerTable.REQUEST_CODE,
|
||||
Intent(what),
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.util.Log
|
|||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
|
@ -117,7 +118,7 @@ private fun isUnderImpl(
|
|||
val View.isRtl: Boolean
|
||||
get() = layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
val Drawable.isRtl: Boolean
|
||||
get() = layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
get() = DrawableCompat.getLayoutDirection(this) == View.LAYOUT_DIRECTION_RTL
|
||||
|
||||
/**
|
||||
* Resolve system bar insets in a version-aware manner. This can be used to apply padding to a view
|
||||
|
|
|
@ -172,12 +172,10 @@
|
|||
<string name="fmt_songs_loaded">已加载 %d 首曲目</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="one">%d 首歌曲</item>
|
||||
<item quantity="other">"%d 首歌曲"</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="fmt_album_count">
|
||||
<item quantity="one">%d 张专辑</item>
|
||||
<item quantity="other">"%d 张专辑"</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<resources>
|
||||
<integer name="detail_app_bar_title_anim_duration">150</integer>
|
||||
|
||||
<!-- FIXME: This is really stupid, figure out how we can unify it with the C object-->
|
||||
<!-- Preference values -->
|
||||
<string-array name="entries_theme">
|
||||
<item>@string/set_theme_auto</item>
|
||||
|
|
|
@ -150,58 +150,8 @@ system events, such as when a button is pressed on a headset. It should **never*
|
|||
|
||||
#### Data Integers
|
||||
Integer representations of data/UI elements are used heavily in Auxio, primarily for efficiency.
|
||||
To prevent any strange bugs, all integer representations must be unique. A table of all current integers used are shown below:
|
||||
|
||||
```
|
||||
0xA0XX | UI Integer Space [Required by android]
|
||||
|
||||
0xA000 | SongViewHolder
|
||||
0xA001 | AlbumViewHolder
|
||||
0xA002 | ArtistViewHolder
|
||||
0xA003 | GenreViewHolder
|
||||
0xA004 | HeaderViewHolder
|
||||
0xA005 | ActionHeaderViewHolder
|
||||
|
||||
0xA006 | AlbumDetailViewHolder
|
||||
0xA007 | AlbumSongViewHolder
|
||||
0xA008 | ArtistDetailViewHolder
|
||||
0xA009 | ArtistAlbumViewHolder
|
||||
0xA00A | ArtistSongViewHolder
|
||||
0xA00B | GenreDetailViewHolder
|
||||
0xA00C | GenreSongViewHolder
|
||||
|
||||
0xA00D | QueueSongViewHolder
|
||||
|
||||
0xA0A0 | Auxio notification code
|
||||
0xA0C0 | Auxio request code
|
||||
|
||||
0xA1XX | Data Integer Space [Stored for IO efficency]
|
||||
|
||||
0xA100 | LoopMode.NONE
|
||||
0xA101 | LoopMode.ALL
|
||||
0xA102 | LoopMode.TRACK
|
||||
|
||||
0xA103 | PlaybackMode.IN_GENRE
|
||||
0xA104 | PlaybackMode.IN_ARTIST
|
||||
0xA105 | PlaybackMode.IN_ALBUM
|
||||
0xA106 | PlaybackMode.ALL_SONGS
|
||||
|
||||
0xA107 | Null DisplayMode [Filter Nothing]
|
||||
0xA108 | DisplayMode.SHOW_GENRES
|
||||
0xA109 | DisplayMode.SHOW_ARTISTS
|
||||
0xA10A | DisplayMode.SHOW_ALBUMS
|
||||
0xA10B | DisplayMode.SHOW_SONGS
|
||||
|
||||
0xA10C | Sort.Name
|
||||
0xA10D | Sort.Artist
|
||||
0xA10E | Sort.Album
|
||||
0xA10F | Sort.Year
|
||||
|
||||
0xA110 | ReplayGainMode.OFF
|
||||
0xA111 | ReplayGainMode.TRACK
|
||||
0xA112 | ReplayGainMode.ALBUM
|
||||
0xA113 | ReplayGainMode.DYNAMIC
|
||||
```
|
||||
To prevent any strange bugs, all integer representations must be unique. To see a table of all current integers, see the `C` class within
|
||||
the project.
|
||||
|
||||
Some datatypes [like `Tab` and `Sort`] have even more fine-grained integer representations for other data. More information can be found in
|
||||
the documentation for those datatypes.
|
||||
|
|
Loading…
Reference in a new issue