Standardize settings entirely
Update the structure of the settings system to do several things: - Finally use int preferences everywhere instead of the mix of strings & ints from before - Create a new preference named `IntListPreference` that enables the use of integers in list preferences - Actually centralize array values and default values into a single integers xml - Isolate all the new migration code into a single file - Refreshed the int tables used by data objects [DB version has been updated, will not update when other changes] The only preference not migrated is doAtEnd since that is being planned to be retired in a future LoopMode update.
This commit is contained in:
parent
355b3885e3
commit
0305eb0beb
29 changed files with 575 additions and 280 deletions
|
@ -9,8 +9,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId "org.oxycblt.auxio"
|
||||
versionName "1.3.3"
|
||||
versionCode 5
|
||||
versionName "1.4.0"
|
||||
versionCode 6
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
|
@ -65,7 +65,7 @@ dependencies {
|
|||
// General
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.activity:activity-ktx:1.2.2"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.2"
|
||||
|
||||
// UI
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||
|
@ -73,7 +73,7 @@ dependencies {
|
|||
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
|
||||
|
||||
// Lifecycle
|
||||
def lifecycle_version = "2.3.0"
|
||||
def lifecycle_version = "2.3.1"
|
||||
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
|
|
|
@ -3,8 +3,6 @@ package org.oxycblt.auxio.database
|
|||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.os.Looper
|
||||
import org.oxycblt.auxio.logE
|
||||
import java.lang.Exception
|
||||
|
||||
/**
|
||||
* Shortcut for querying all items in a database and running [block] with the cursor returned.
|
||||
|
|
|
@ -228,7 +228,7 @@ class PlaybackStateDatabase(context: Context) :
|
|||
|
||||
companion object {
|
||||
const val DB_NAME = "auxio_state_database.db"
|
||||
const val DB_VERSION = 3
|
||||
const val DB_VERSION = 4
|
||||
|
||||
const val TABLE_NAME_STATE = "playback_state_table"
|
||||
const val TABLE_NAME_QUEUE = "queue_table"
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.net.Uri
|
|||
// --- MUSIC MODELS ---
|
||||
|
||||
// TODO: Implement some kind of hash system, removing the need to redundant names but alsow without the volitility of id
|
||||
// They need to be completely unique, however, and from whatever information I have about them.
|
||||
// They need to be completely unique, however, and from whatever information I have about them on creation
|
||||
|
||||
/**
|
||||
* The base data object for all music.
|
||||
|
|
|
@ -31,9 +31,9 @@ enum class LoopMode {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_NONE = 0xA050
|
||||
private const val CONST_ONCE = 0xA051
|
||||
private const val CONST_INFINITE = 0xA052
|
||||
const val CONST_NONE = 0xA100
|
||||
const val CONST_ONCE = 0xA101
|
||||
const val CONST_INFINITE = 0xA102
|
||||
|
||||
/**
|
||||
* Convert an int [constant] into a LoopMode
|
||||
|
|
|
@ -28,10 +28,10 @@ enum class PlaybackMode {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_IN_ARTIST = 0xA040
|
||||
private const val CONST_IN_GENRE = 0xA041
|
||||
private const val CONST_IN_ALBUM = 0xA042
|
||||
private const val CONST_ALL_SONGS = 0xA043
|
||||
const val CONST_IN_GENRE = 0xA103
|
||||
const val CONST_IN_ARTIST = 0xA104
|
||||
const val CONST_IN_ALBUM = 0xA105
|
||||
const val CONST_ALL_SONGS = 0xA106
|
||||
|
||||
/**
|
||||
* Get a [PlaybackMode] for an int [constant]
|
||||
|
|
|
@ -810,6 +810,10 @@ class PlaybackStateManager private constructor() {
|
|||
companion object {
|
||||
private const val REWIND_THRESHOLD = 3000L
|
||||
|
||||
const val AT_END_LOOP_PAUSE = 0
|
||||
const val AT_END_LOOP = 1
|
||||
const val AT_END_STOP = 2
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: PlaybackStateManager? = null
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ class PlaybackNotification private constructor(
|
|||
companion object {
|
||||
const val CHANNEL_ID = "CHANNEL_AUXIO_PLAYBACK"
|
||||
const val NOTIFICATION_ID = 0xA0A0
|
||||
const val REQUEST_CODE = 0xA0C0
|
||||
const val REQUEST_CODE = 0xA0AA
|
||||
|
||||
// Build type is added to the codes so that dual release/debug installations dont conflict
|
||||
// with each other.
|
||||
|
|
|
@ -28,7 +28,23 @@ enum class DisplayMode(@DrawableRes val iconRes: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
SHOW_ALL -> CONST_SHOW_ALL
|
||||
SHOW_GENRES -> CONST_SHOW_GENRES
|
||||
SHOW_ARTISTS -> CONST_SHOW_ARTISTS
|
||||
SHOW_ALBUMS -> CONST_SHOW_ALBUMS
|
||||
SHOW_SONGS -> CONST_SHOW_SONGS
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CONST_SHOW_ALL = 0xA107
|
||||
const val CONST_SHOW_GENRES = 0xA108
|
||||
const val CONST_SHOW_ARTISTS = 0xA109
|
||||
const val CONST_SHOW_ALBUMS = 0xA10A
|
||||
const val CONST_SHOW_SONGS = 0xA10B
|
||||
|
||||
/**
|
||||
* A valueOf wrapper that will return a default value if given a null/invalid string.
|
||||
*/
|
||||
|
@ -55,5 +71,21 @@ enum class DisplayMode(@DrawableRes val iconRes: Int) {
|
|||
else -> SHOW_ALL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an enum for an int constant
|
||||
* @return The [DisplayMode] if the constant is valid, null otherwise.
|
||||
*/
|
||||
fun fromInt(value: Int): DisplayMode? {
|
||||
return when (value) {
|
||||
CONST_SHOW_ALL -> SHOW_ALL
|
||||
CONST_SHOW_GENRES -> SHOW_GENRES
|
||||
CONST_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
CONST_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
CONST_SHOW_SONGS -> SHOW_SONGS
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,13 +174,11 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_NONE = 0xA060
|
||||
private const val CONST_ALPHA_UP = 0xA061
|
||||
private const val CONST_ALPHA_DOWN = 0xA062
|
||||
private const val CONST_NUMERIC_UP = 0xA063
|
||||
private const val CONST_NUMERIC_DOWN = 0xA065
|
||||
|
||||
const val CONST_SORT_DEFAULT = CONST_ALPHA_DOWN
|
||||
const val CONST_NONE = 0xA10C
|
||||
const val CONST_ALPHA_UP = 0xA10D
|
||||
const val CONST_ALPHA_DOWN = 0xA10E
|
||||
const val CONST_NUMERIC_UP = 0xA10F
|
||||
const val CONST_NUMERIC_DOWN = 0xA110
|
||||
|
||||
/**
|
||||
* Get an enum for an int constant
|
||||
|
|
|
@ -14,23 +14,6 @@ import org.oxycblt.auxio.music.Header
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
||||
/*
|
||||
* A table of all ViewHolder codes. Please add to these so that all viewholder codes are unique.
|
||||
* SongViewHolder -> 0xA000
|
||||
* AlbumViewHolder -> 0xA001
|
||||
* ArtistViewHolder -> 0xA002
|
||||
* GenreViewHolder -> 0xA003
|
||||
* HeaderViewHolder -> 0xA004
|
||||
* QueueSongViewHolder -> 0xA005
|
||||
* UserQueueHeaderViewHolder -> 0xA006
|
||||
* AlbumHeaderViewHolder -> 0xA007
|
||||
* AlbumSongViewHolder -> 0xA008
|
||||
* ArtistHeaderViewHolder -> 0xA009
|
||||
* ArtistAlbumViewHolder -> 0xA00A
|
||||
* GenreHeaderViewHolder -> 0xA00B
|
||||
* GenreSongViewHolder -> 0xA00C
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Shared ViewHolder for a [Song]. Instantiation should be done with [from].
|
||||
*/
|
||||
|
|
|
@ -4,21 +4,6 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
/**
|
||||
* Convert a string representing a theme entry name to an actual theme int that can be used.
|
||||
* This is only done because PreferenceFragment does not like int arrays for some...reason.
|
||||
* @return The proper theme int for this value.
|
||||
*/
|
||||
fun String.toThemeInt(): Int {
|
||||
return when (this) {
|
||||
SettingsManager.EntryValues.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
SettingsManager.EntryValues.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
SettingsManager.EntryValues.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an theme integer into an icon that can be used.
|
||||
* @return An icon for this theme.
|
||||
|
|
175
app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt
Normal file
175
app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt
Normal file
|
@ -0,0 +1,175 @@
|
|||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
|
||||
// A couple of utils for migrating from old settings values to the new the new
|
||||
// formats used in 1.3.2 & 1.4.0
|
||||
|
||||
fun handleThemeCompat(prefs: SharedPreferences): Int {
|
||||
if (prefs.contains(OldKeys.KEY_THEME)) {
|
||||
// Before the creation of IntListPreference, I used strings to represent the themes.
|
||||
// I no longer need to do this.
|
||||
val newValue = when (prefs.getString(OldKeys.KEY_THEME, EntryValues.THEME_AUTO)) {
|
||||
EntryValues.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
EntryValues.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
EntryValues.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
|
||||
else -> error("Invalid theme")
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_THEME, newValue)
|
||||
remove(OldKeys.KEY_THEME)
|
||||
apply()
|
||||
}
|
||||
|
||||
return newValue
|
||||
}
|
||||
|
||||
return prefs.getInt(SettingsManager.KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
|
||||
fun handleAccentCompat(prefs: SharedPreferences): Accent {
|
||||
if (prefs.contains(OldKeys.KEY_ACCENT)) {
|
||||
var accent = prefs.getInt(OldKeys.KEY_ACCENT, 5)
|
||||
|
||||
// Correct any accents over yellow to their correct positions
|
||||
if (accent > 12) {
|
||||
accent--
|
||||
}
|
||||
|
||||
// Correct neutral accents to the closest accent [Grey]
|
||||
if (accent == 18) {
|
||||
accent = 16
|
||||
}
|
||||
|
||||
// If there are still any issues with indices, just correct them so a crash doesnt occur.
|
||||
if (accent > ACCENTS.lastIndex) {
|
||||
accent = ACCENTS.lastIndex
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_ACCENT, accent)
|
||||
remove(OldKeys.KEY_ACCENT)
|
||||
apply()
|
||||
}
|
||||
|
||||
return ACCENTS[accent]
|
||||
}
|
||||
|
||||
return ACCENTS[prefs.getInt(SettingsManager.KEY_ACCENT, 5)]
|
||||
}
|
||||
|
||||
fun handleLibDisplayCompat(prefs: SharedPreferences): DisplayMode {
|
||||
if (prefs.contains(OldKeys.KEY_LIB_MODE)) {
|
||||
val mode = handleStringDisplayMode(
|
||||
prefs.getString(OldKeys.KEY_LIB_MODE, EntryValues.SHOW_ARTISTS),
|
||||
) ?: DisplayMode.SHOW_ARTISTS
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_LIB_DISPLAY_MODE, mode.toInt())
|
||||
remove(OldKeys.KEY_LIB_MODE)
|
||||
apply()
|
||||
}
|
||||
|
||||
return mode
|
||||
}
|
||||
|
||||
return DisplayMode.fromInt(
|
||||
prefs.getInt(SettingsManager.KEY_LIB_DISPLAY_MODE, DisplayMode.CONST_SHOW_ARTISTS)
|
||||
) ?: DisplayMode.SHOW_ARTISTS
|
||||
}
|
||||
|
||||
fun handleSongPlayModeCompat(prefs: SharedPreferences): PlaybackMode {
|
||||
if (prefs.contains(OldKeys.KEY_SONG_PLAYBACK_MODE)) {
|
||||
val mode = when (prefs.getString(OldKeys.KEY_SONG_PLAYBACK_MODE, EntryValues.ALL_SONGS)) {
|
||||
EntryValues.IN_GENRE -> PlaybackMode.IN_GENRE
|
||||
EntryValues.IN_ARTIST -> PlaybackMode.IN_ARTIST
|
||||
EntryValues.IN_ALBUM -> PlaybackMode.IN_ALBUM
|
||||
EntryValues.ALL_SONGS -> PlaybackMode.ALL_SONGS
|
||||
|
||||
else -> PlaybackMode.ALL_SONGS
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_SONG_PLAYBACK_MODE, mode.toInt())
|
||||
remove(OldKeys.KEY_SONG_PLAYBACK_MODE)
|
||||
apply()
|
||||
}
|
||||
|
||||
return mode
|
||||
}
|
||||
|
||||
return PlaybackMode.fromInt(
|
||||
prefs.getInt(SettingsManager.KEY_SONG_PLAYBACK_MODE, PlaybackMode.CONST_ALL_SONGS)
|
||||
) ?: PlaybackMode.ALL_SONGS
|
||||
}
|
||||
|
||||
fun handleSearchModeCompat(prefs: SharedPreferences): DisplayMode {
|
||||
if (prefs.contains(OldKeys.KEY_SEARCH_FILTER)) {
|
||||
val mode = handleStringDisplayMode(
|
||||
prefs.getString(OldKeys.KEY_SEARCH_FILTER, EntryValues.SHOW_ALL)
|
||||
) ?: DisplayMode.SHOW_ALL
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_SEARCH_FILTER_MODE, mode.toInt())
|
||||
remove(OldKeys.KEY_SEARCH_FILTER)
|
||||
apply()
|
||||
}
|
||||
|
||||
return mode
|
||||
}
|
||||
|
||||
return DisplayMode.fromInt(
|
||||
prefs.getInt(SettingsManager.KEY_SEARCH_FILTER_MODE, DisplayMode.CONST_SHOW_ALL)
|
||||
) ?: DisplayMode.SHOW_ARTISTS
|
||||
}
|
||||
|
||||
private fun handleStringDisplayMode(string: String?): DisplayMode? {
|
||||
return when (string) {
|
||||
EntryValues.SHOW_GENRES -> DisplayMode.SHOW_GENRES
|
||||
EntryValues.SHOW_ARTISTS -> DisplayMode.SHOW_ARTISTS
|
||||
EntryValues.SHOW_ALBUMS -> DisplayMode.SHOW_ALBUMS
|
||||
EntryValues.SHOW_SONGS -> DisplayMode.SHOW_SONGS
|
||||
EntryValues.SHOW_ALL -> DisplayMode.SHOW_ALL
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache of the old keys used in Auxio.
|
||||
*/
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT = "KEY_ACCENT"
|
||||
const val KEY_THEME = "KEY_THEME"
|
||||
const val KEY_LIB_MODE = "KEY_LIBRARY_DISPLAY_MODE"
|
||||
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE"
|
||||
const val KEY_SEARCH_FILTER = "KEY_SEARCH"
|
||||
}
|
||||
|
||||
/**
|
||||
* Static cache of old string values used in Auxio
|
||||
*/
|
||||
private object EntryValues {
|
||||
const val THEME_AUTO = "AUTO"
|
||||
const val THEME_LIGHT = "LIGHT"
|
||||
const val THEME_DARK = "DARK"
|
||||
|
||||
const val SHOW_GENRES = "SHOW_GENRES"
|
||||
const val SHOW_ARTISTS = "SHOW_ARTISTS"
|
||||
const val SHOW_ALBUMS = "SHOW_ALBUMS"
|
||||
const val SHOW_SONGS = "SHOW_SONGS"
|
||||
const val SHOW_ALL = "SHOW_ALL"
|
||||
|
||||
const val IN_GENRE = "IN_GENRE"
|
||||
const val IN_ARTIST = "IN_ARTIST"
|
||||
const val IN_ALBUM = "IN_ALBUM"
|
||||
const val ALL_SONGS = "ALL_SONGS"
|
||||
}
|
|
@ -15,12 +15,13 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
|
|||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.settings.accent.AccentDialog
|
||||
import org.oxycblt.auxio.settings.blacklist.BlacklistDialog
|
||||
import org.oxycblt.auxio.settings.ui.IntListPrefDialog
|
||||
import org.oxycblt.auxio.settings.ui.IntListPreference
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.createToast
|
||||
|
||||
/**
|
||||
* The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat].
|
||||
* TODO: Roll your own ListPreference that supports int prefs [and is a bottom sheet]
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
@Suppress("UNUSED")
|
||||
|
@ -44,6 +45,13 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
setPreferencesFromResource(R.xml.prefs_main, rootKey)
|
||||
}
|
||||
|
||||
override fun onDisplayPreferenceDialog(preference: Preference?) {
|
||||
if (preference is IntListPreference) {
|
||||
IntListPrefDialog(preference).show(childFragmentManager, IntListPrefDialog.TAG)
|
||||
} else {
|
||||
super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Recursively call [handlePreference] on a preference.
|
||||
*/
|
||||
|
@ -64,44 +72,35 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
private fun handlePreference(pref: Preference) {
|
||||
pref.apply {
|
||||
when (key) {
|
||||
SettingsManager.Keys.KEY_THEME -> {
|
||||
SettingsManager.KEY_THEME -> {
|
||||
setIcon(AppCompatDelegate.getDefaultNightMode().toThemeIcon())
|
||||
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, value ->
|
||||
AppCompatDelegate.setDefaultNightMode((value as String).toThemeInt())
|
||||
|
||||
AppCompatDelegate.setDefaultNightMode(value as Int)
|
||||
setIcon(AppCompatDelegate.getDefaultNightMode().toThemeIcon())
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_ACCENT -> {
|
||||
SettingsManager.KEY_ACCENT -> {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
AccentDialog().show(childFragmentManager, TAG_ACCENT_DIALOG)
|
||||
AccentDialog().show(childFragmentManager, AccentDialog.TAG)
|
||||
true
|
||||
}
|
||||
|
||||
summary = Accent.get().getDetailedSummary(context)
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_EDGE_TO_EDGE -> {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
requireActivity().recreate()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_LIBRARY_DISPLAY_MODE -> {
|
||||
SettingsManager.KEY_LIB_DISPLAY_MODE -> {
|
||||
setIcon(settingsManager.libraryDisplayMode.iconRes)
|
||||
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, value ->
|
||||
setIcon(DisplayMode.valueOfOrFallback(value as String).iconRes)
|
||||
setIcon(DisplayMode.fromInt(value as Int)!!.iconRes)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_SHOW_COVERS -> {
|
||||
SettingsManager.KEY_SHOW_COVERS -> {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
Coil.imageLoader(requireContext()).apply {
|
||||
bitmapPool.clear()
|
||||
|
@ -114,7 +113,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_QUALITY_COVERS -> {
|
||||
SettingsManager.KEY_QUALITY_COVERS -> {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
// Clear out any cached images, before recreating the activity
|
||||
Coil.imageLoader(requireContext()).apply {
|
||||
|
@ -128,7 +127,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_SAVE_STATE -> {
|
||||
SettingsManager.KEY_SAVE_STATE -> {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
playbackModel.savePlaybackState(requireContext()) {
|
||||
getString(R.string.label_state_saved).createToast(requireContext())
|
||||
|
@ -137,17 +136,13 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
SettingsManager.Keys.KEY_BLACKLIST -> {
|
||||
SettingsManager.KEY_BLACKLIST -> {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
BlacklistDialog().show(childFragmentManager, TAG_ACCENT_DIALOG)
|
||||
BlacklistDialog().show(childFragmentManager, BlacklistDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_ACCENT_DIALOG = "ACCENT_DIALOG"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ package org.oxycblt.auxio.settings
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
|
@ -16,9 +16,16 @@ import org.oxycblt.auxio.ui.Accent
|
|||
*/
|
||||
class SettingsManager private constructor(context: Context) :
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
init {
|
||||
// If needed, we need to touch the song mode pref so that it migrates before something
|
||||
// else is written by the Preference.
|
||||
if (!sharedPrefs.contains(KEY_SONG_PLAYBACK_MODE)) {
|
||||
handleSongPlayModeCompat(sharedPrefs)
|
||||
}
|
||||
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
|
@ -26,125 +33,93 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
/** The current theme */
|
||||
val theme: Int
|
||||
get() = sharedPrefs.getString(Keys.KEY_THEME, EntryValues.THEME_AUTO)!!.toThemeInt()
|
||||
get() = handleThemeCompat(sharedPrefs)
|
||||
|
||||
/** The current accent. */
|
||||
var accent: Accent
|
||||
get() {
|
||||
@Suppress("DEPRECATION")
|
||||
if (sharedPrefs.contains(Keys.KEY_ACCENT_OLD)) {
|
||||
logD("Migrating from old accent to new accent.")
|
||||
|
||||
val newAccent = handleAccentCompat(
|
||||
sharedPrefs.getInt(Keys.KEY_ACCENT_OLD, 5)
|
||||
)
|
||||
|
||||
// When converted, write them to the new accent pref and delete the old one.
|
||||
sharedPrefs.edit()
|
||||
.putInt(Keys.KEY_ACCENT, newAccent)
|
||||
.remove(Keys.KEY_ACCENT_OLD)
|
||||
.apply()
|
||||
}
|
||||
|
||||
return ACCENTS[sharedPrefs.getInt(Keys.KEY_ACCENT, 5)]
|
||||
}
|
||||
get() = handleAccentCompat(sharedPrefs)
|
||||
set(value) {
|
||||
val accentIndex = ACCENTS.indexOf(value)
|
||||
|
||||
check(accentIndex != -1) { "Invalid accent" }
|
||||
|
||||
sharedPrefs.edit()
|
||||
.putInt(Keys.KEY_ACCENT, accentIndex)
|
||||
.apply()
|
||||
sharedPrefs.edit {
|
||||
putInt(KEY_ACCENT, accentIndex)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether to colorize the notification */
|
||||
val colorizeNotif: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_COLORIZE_NOTIFICATION, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_COLORIZE_NOTIFICATION, true)
|
||||
|
||||
/**
|
||||
* Whether to display the LoopMode or the shuffle status on the notification.
|
||||
* False if loop, true if shuffle.
|
||||
*/
|
||||
val useAltNotifAction: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_USE_ALT_NOTIFICATION_ACTION, false)
|
||||
get() = sharedPrefs.getBoolean(KEY_USE_ALT_NOTIFICATION_ACTION, false)
|
||||
|
||||
/** What to display on the library. */
|
||||
val libraryDisplayMode: DisplayMode
|
||||
get() = DisplayMode.valueOfOrFallback(
|
||||
sharedPrefs.getString(
|
||||
Keys.KEY_LIBRARY_DISPLAY_MODE,
|
||||
DisplayMode.SHOW_ARTISTS.toString()
|
||||
)
|
||||
)
|
||||
get() = handleLibDisplayCompat(sharedPrefs)
|
||||
|
||||
/**
|
||||
* Whether to even loading embedded covers
|
||||
* TODO: Make the UI result of this better?
|
||||
*/
|
||||
val showCovers: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_SHOW_COVERS, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_SHOW_COVERS, true)
|
||||
|
||||
/** Whether to ignore MediaStore covers */
|
||||
val useQualityCovers: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_QUALITY_COVERS, false)
|
||||
get() = sharedPrefs.getBoolean(KEY_QUALITY_COVERS, false)
|
||||
|
||||
/** Whether to do Audio focus. */
|
||||
val doAudioFocus: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_AUDIO_FOCUS, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_AUDIO_FOCUS, true)
|
||||
|
||||
/** Whether to resume/stop playback when a headset is connected/disconnected. */
|
||||
val doPlugMgt: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_PLUG_MANAGEMENT, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_PLUG_MANAGEMENT, true)
|
||||
|
||||
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
||||
val songPlaybackMode: PlaybackMode
|
||||
get() = PlaybackMode.valueOfOrFallback(
|
||||
sharedPrefs.getString(
|
||||
Keys.KEY_SONG_PLAYBACK_MODE,
|
||||
PlaybackMode.ALL_SONGS.toString()
|
||||
)
|
||||
)
|
||||
get() = handleSongPlayModeCompat(sharedPrefs)
|
||||
|
||||
/** What to do at the end of a playlist. */
|
||||
val doAtEnd: String
|
||||
get() = sharedPrefs.getString(Keys.KEY_AT_END, EntryValues.AT_END_LOOP_PAUSE)
|
||||
get() = sharedPrefs.getString(KEY_AT_END, EntryValues.AT_END_LOOP_PAUSE)
|
||||
?: EntryValues.AT_END_LOOP_PAUSE
|
||||
|
||||
/** Whether shuffle should stay on when a new song is selected. */
|
||||
val keepShuffle: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_KEEP_SHUFFLE, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_KEEP_SHUFFLE, true)
|
||||
|
||||
/** Whether to rewind when the back button is pressed. */
|
||||
val rewindWithPrev: Boolean
|
||||
get() = sharedPrefs.getBoolean(Keys.KEY_PREV_REWIND, true)
|
||||
get() = sharedPrefs.getBoolean(KEY_PREV_REWIND, true)
|
||||
|
||||
/** The current [SortMode] of the library. */
|
||||
var librarySortMode: SortMode
|
||||
get() = SortMode.fromInt(
|
||||
sharedPrefs.getInt(
|
||||
Keys.KEY_LIBRARY_SORT_MODE, SortMode.CONST_SORT_DEFAULT
|
||||
)
|
||||
sharedPrefs.getInt(KEY_LIBRARY_SORT_MODE, SortMode.CONST_ALPHA_DOWN)
|
||||
) ?: SortMode.ALPHA_DOWN
|
||||
|
||||
set(value) {
|
||||
sharedPrefs.edit()
|
||||
.putInt(Keys.KEY_LIBRARY_SORT_MODE, value.toInt())
|
||||
.apply()
|
||||
sharedPrefs.edit {
|
||||
putInt(KEY_LIBRARY_SORT_MODE, value.toInt())
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
/** The current filter mode of the search tab */
|
||||
var searchFilterMode: DisplayMode
|
||||
get() = DisplayMode.valueOfOrFallback(
|
||||
sharedPrefs.getString(
|
||||
Keys.KEY_SEARCH_FILTER_MODE, DisplayMode.SHOW_ALL.toString()
|
||||
),
|
||||
fallback = DisplayMode.SHOW_ALL
|
||||
)
|
||||
get() = handleSearchModeCompat(sharedPrefs)
|
||||
set(value) {
|
||||
sharedPrefs.edit()
|
||||
.putString(Keys.KEY_SEARCH_FILTER_MODE, value.toString())
|
||||
.apply()
|
||||
sharedPrefs.edit {
|
||||
putInt(KEY_SEARCH_FILTER_MODE, value.toInt())
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
// --- CALLBACKS ---
|
||||
|
@ -163,89 +138,31 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
when (key) {
|
||||
Keys.KEY_COLORIZE_NOTIFICATION -> callbacks.forEach {
|
||||
KEY_COLORIZE_NOTIFICATION -> callbacks.forEach {
|
||||
it.onColorizeNotifUpdate(colorizeNotif)
|
||||
}
|
||||
|
||||
Keys.KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach {
|
||||
KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach {
|
||||
it.onNotifActionUpdate(useAltNotifAction)
|
||||
}
|
||||
|
||||
Keys.KEY_LIBRARY_DISPLAY_MODE -> callbacks.forEach {
|
||||
KEY_LIB_DISPLAY_MODE -> callbacks.forEach {
|
||||
it.onLibDisplayModeUpdate(libraryDisplayMode)
|
||||
}
|
||||
|
||||
Keys.KEY_SHOW_COVERS -> callbacks.forEach {
|
||||
KEY_SHOW_COVERS -> callbacks.forEach {
|
||||
it.onShowCoverUpdate(showCovers)
|
||||
}
|
||||
|
||||
Keys.KEY_QUALITY_COVERS -> callbacks.forEach {
|
||||
KEY_QUALITY_COVERS -> callbacks.forEach {
|
||||
it.onQualityCoverUpdate(useQualityCovers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the old accent format of <1.3.1 to the accent format of 1.3.2-Onwards,
|
||||
* where many accents were changed or removed.
|
||||
*/
|
||||
private fun handleAccentCompat(oldAccent: Int): Int {
|
||||
var newAccent = oldAccent
|
||||
|
||||
// Correct any accents over yellow to their correct positions
|
||||
if (oldAccent > 12) {
|
||||
newAccent--
|
||||
}
|
||||
|
||||
// Correct neutral accents to the closest accent [Grey]
|
||||
if (newAccent == 18) {
|
||||
newAccent = 16
|
||||
}
|
||||
|
||||
// If there are still any issues with indices, just correct them so a crash doesnt occur.
|
||||
if (newAccent > ACCENTS.lastIndex) {
|
||||
newAccent = ACCENTS.lastIndex
|
||||
}
|
||||
|
||||
return newAccent
|
||||
}
|
||||
|
||||
/**
|
||||
* SharedPreferences keys.
|
||||
*/
|
||||
object Keys {
|
||||
const val KEY_THEME = "KEY_THEME"
|
||||
const val KEY_ACCENT = "KEY_ACCENT2"
|
||||
const val KEY_EDGE_TO_EDGE = "KEY_EDGE"
|
||||
const val KEY_LIBRARY_DISPLAY_MODE = "KEY_LIBRARY_DISPLAY_MODE"
|
||||
const val KEY_SHOW_COVERS = "KEY_SHOW_COVERS"
|
||||
const val KEY_QUALITY_COVERS = "KEY_QUALITY_COVERS"
|
||||
const val KEY_COLORIZE_NOTIFICATION = "KEY_COLOR_NOTIF"
|
||||
const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION"
|
||||
const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
|
||||
const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT"
|
||||
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE"
|
||||
const val KEY_AT_END = "KEY_AT_END"
|
||||
const val KEY_KEEP_SHUFFLE = "KEY_KEEP_SHUFFLE"
|
||||
const val KEY_PREV_REWIND = "KEY_PREV_REWIND"
|
||||
|
||||
const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE"
|
||||
const val KEY_SEARCH_FILTER_MODE = "KEY_SEARCH"
|
||||
const val KEY_SAVE_STATE = "KEY_SAVE_STATE"
|
||||
const val KEY_BLACKLIST = "KEY_BLACKLIST"
|
||||
|
||||
@Deprecated("Use the new KEY_ACCENT instead.")
|
||||
const val KEY_ACCENT_OLD = "KEY_ACCENT"
|
||||
}
|
||||
|
||||
/**
|
||||
* Values for some settings entries that arent important enough to recieve an enum.
|
||||
*/
|
||||
object EntryValues {
|
||||
const val THEME_AUTO = "AUTO"
|
||||
const val THEME_LIGHT = "LIGHT"
|
||||
const val THEME_DARK = "DARK"
|
||||
|
||||
/** Pause and loop at the end. Similar to Spotify. */
|
||||
const val AT_END_LOOP_PAUSE = "LOOP_PAUSE"
|
||||
|
||||
|
@ -270,6 +187,29 @@ class SettingsManager private constructor(context: Context) :
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_THEME = "KEY_THEME2"
|
||||
const val KEY_ACCENT = "KEY_ACCENT2"
|
||||
|
||||
const val KEY_LIB_DISPLAY_MODE = "KEY_LIB_MODE"
|
||||
const val KEY_SHOW_COVERS = "KEY_SHOW_COVERS"
|
||||
const val KEY_QUALITY_COVERS = "KEY_QUALITY_COVERS"
|
||||
const val KEY_COLORIZE_NOTIFICATION = "KEY_COLOR_NOTIF"
|
||||
const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION"
|
||||
|
||||
const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
|
||||
const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT"
|
||||
|
||||
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2"
|
||||
const val KEY_AT_END = "KEY_AT_END"
|
||||
const val KEY_KEEP_SHUFFLE = "KEY_KEEP_SHUFFLE"
|
||||
const val KEY_PREV_REWIND = "KEY_PREV_REWIND"
|
||||
|
||||
const val KEY_SAVE_STATE = "KEY_SAVE_STATE"
|
||||
const val KEY_BLACKLIST = "KEY_BLACKLIST"
|
||||
|
||||
const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE"
|
||||
const val KEY_SEARCH_FILTER_MODE = "KEY_SEARCH_FILTER"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: SettingsManager? = null
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ class AccentDialog : LifecycleDialog() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TAG_ACCENT_DIALOG"
|
||||
const val KEY_PENDING_ACCENT = "AXKEY_PEND_ACCENT"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,4 +156,8 @@ class BlacklistDialog : LifecycleDialog() {
|
|||
private fun getRootPath(): String {
|
||||
return Environment.getExternalStorageDirectory().absolutePath
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TAG_BLACKLIST_DIALOG"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.oxycblt.auxio.settings.ui
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
||||
class IntListPrefDialog(private val pref: IntListPreference) : LifecycleDialog() {
|
||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||
builder.setTitle(pref.title)
|
||||
|
||||
builder.setSingleChoiceItems(pref.entries, pref.getValueIndex()) { _, index ->
|
||||
pref.setValue(pref.values[index])
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
builder.setNegativeButton(android.R.string.cancel, null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "TAG_INT_PREF_DIALOG"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.oxycblt.auxio.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.DialogPreference
|
||||
import org.oxycblt.auxio.R
|
||||
import androidx.preference.R as prefR
|
||||
|
||||
class IntListPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = prefR.attr.dialogPreferenceStyle,
|
||||
defStyleRes: Int = 0
|
||||
) : DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
|
||||
val entries: Array<CharSequence>
|
||||
val values: IntArray
|
||||
var currentValue: Int? = null
|
||||
private set
|
||||
|
||||
private val default: Int
|
||||
|
||||
init {
|
||||
val prefAttrs = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.IntListPreference, defStyleAttr, defStyleRes
|
||||
)
|
||||
|
||||
entries = prefAttrs.getTextArray(R.styleable.IntListPreference_entries)
|
||||
|
||||
values = context.resources.getIntArray(
|
||||
prefAttrs.getResourceId(R.styleable.IntListPreference_entryValues, -1)
|
||||
)
|
||||
|
||||
default = prefAttrs.getInt(prefR.styleable.Preference_defaultValue, Int.MIN_VALUE)
|
||||
|
||||
prefAttrs.recycle()
|
||||
|
||||
summaryProvider = IntListSummaryProvider()
|
||||
}
|
||||
|
||||
override fun onGetDefaultValue(a: TypedArray, index: Int) = a.getInt(index, -1)
|
||||
|
||||
override fun onSetInitialValue(defaultValue: Any?) {
|
||||
super.onSetInitialValue(defaultValue)
|
||||
|
||||
if (defaultValue != null) {
|
||||
// If were given a default value, we need to assign it.
|
||||
setValue(defaultValue as Int)
|
||||
} else {
|
||||
currentValue = getPersistedInt(default)
|
||||
}
|
||||
}
|
||||
|
||||
fun getValueIndex(): Int {
|
||||
val curValue = currentValue
|
||||
|
||||
if (curValue != null) {
|
||||
return values.indexOf(curValue)
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
fun setValue(value: Int) {
|
||||
if (value != currentValue) {
|
||||
currentValue = value
|
||||
|
||||
callChangeListener(value)
|
||||
persistInt(value)
|
||||
notifyChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class IntListSummaryProvider : SummaryProvider<IntListPreference> {
|
||||
override fun provideSummary(preference: IntListPreference?): CharSequence {
|
||||
val index = getValueIndex()
|
||||
|
||||
if (index != -1) {
|
||||
return entries[index]
|
||||
}
|
||||
|
||||
return context.getString(prefR.string.not_set)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ data class Accent(@ColorRes val color: Int, @StyleRes val theme: Int, @StringRes
|
|||
/**
|
||||
* Get a [ColorStateList] of the accent
|
||||
*/
|
||||
fun getStateList(context: Context): ColorStateList = color.toStateList(context)
|
||||
fun getStateList(context: Context) = color.toStateList(context)
|
||||
|
||||
/**
|
||||
* Get the name (in bold) and the hex value of a accent.
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
<string name="setting_theme_day">Hell</string>
|
||||
<string name="setting_theme_night">Dunkel</string>
|
||||
<string name="setting_accent">Akzentfarbe</string>
|
||||
<string name="setting_accent_unknown">Unbekannte Akzentfarbe</string>
|
||||
|
||||
<string name="setting_display">Anzeige</string>
|
||||
<string name="setting_lib_display">Musikbibliothekitems</string>
|
||||
|
@ -97,7 +96,7 @@
|
|||
<string name="setting_content_save">Wiedergabezustand abspeichern</string>
|
||||
<string name="setting_content_save_desc">Der aktuell Wiedergabezustand jetzt abspeichern</string>
|
||||
<string name="setting_content_blacklist">Ausgeschlossene Ordner</string>
|
||||
<string name="setting_content_blacklist_desc">Die Inhalte der ausgeschlossene Ordner sind versteckt von deinen Musikbibliothek</string>
|
||||
<string name="setting_content_blacklist_desc">Die Inhalte der ausgeschlossene Ordner sind versteckt von deiner Musikbibliothek</string>
|
||||
|
||||
<!-- Error Namespace | Error Labels -->
|
||||
<string name="error_no_music">Keine Musik gefunden</string>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<item>@string/setting_theme_night</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="values_theme" translatable="false">
|
||||
<item>AUTO</item>
|
||||
<item>LIGHT</item>
|
||||
<item>DARK</item>
|
||||
</string-array>
|
||||
<integer-array name="values_theme">
|
||||
<item>@integer/theme_auto</item>
|
||||
<item>@integer/theme_light</item>
|
||||
<item>@integer/theme_dark</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="entries_lib_display">
|
||||
<item>@string/label_genres</item>
|
||||
|
@ -18,11 +18,11 @@
|
|||
<item>@string/label_albums</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="values_lib_display" translatable="false">
|
||||
<item>SHOW_GENRES</item>
|
||||
<item>SHOW_ARTISTS</item>
|
||||
<item>SHOW_ALBUMS</item>
|
||||
</string-array>
|
||||
<integer-array name="values_lib_display">
|
||||
<item>@integer/display_genre</item>
|
||||
<item>@integer/display_artist</item>
|
||||
<item>@integer/display_album</item>
|
||||
</integer-array>
|
||||
|
||||
<array name="entries_song_playback_mode">
|
||||
<item>@string/label_play_all_songs</item>
|
||||
|
@ -31,11 +31,11 @@
|
|||
<item>@string/label_play_genre</item>
|
||||
</array>
|
||||
|
||||
<string-array name="values_song_playback_mode" translatable="false">
|
||||
<item>ALL_SONGS</item>
|
||||
<item>IN_ARTIST</item>
|
||||
<item>IN_ALBUM</item>
|
||||
<item>IN_GENRE</item>
|
||||
<string-array name="values_song_playback_mode">
|
||||
<item>@integer/play_mode_songs</item>
|
||||
<item>@integer/play_mode_artist</item>
|
||||
<item>@integer/play_mode_album</item>
|
||||
<item>@integer/play_mode_genre</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="entries_at_end">
|
||||
|
@ -44,7 +44,7 @@
|
|||
<item>@string/setting_behavior_end_stop</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="values_at_end" translatable="false">
|
||||
<string-array name="values_at_end">
|
||||
<item>LOOP_PAUSE</item>
|
||||
<item>LOOP</item>
|
||||
<item>STOP</item>
|
||||
|
|
15
app/src/main/res/values/ints.xml
Normal file
15
app/src/main/res/values/ints.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="theme_auto">-1</integer>
|
||||
<integer name="theme_light">1</integer>
|
||||
<integer name="theme_dark">2</integer>
|
||||
|
||||
<integer name="display_genre">0xA108</integer>
|
||||
<integer name="display_artist">0xA109</integer>
|
||||
<integer name="display_album">0xA10A</integer>
|
||||
|
||||
<integer name="play_mode_genre">0xA103</integer>
|
||||
<integer name="play_mode_artist">0xA104</integer>
|
||||
<integer name="play_mode_album">0xA105</integer>
|
||||
<integer name="play_mode_songs">0xA106</integer>
|
||||
</resources>
|
|
@ -63,7 +63,6 @@
|
|||
<string name="setting_theme_day">Light</string>
|
||||
<string name="setting_theme_night">Dark</string>
|
||||
<string name="setting_accent">Accent</string>
|
||||
<string name="setting_accent_unknown">Unknown Accent</string>
|
||||
|
||||
<string name="setting_display">Display</string>
|
||||
<string name="setting_lib_display">Library Items</string>
|
||||
|
|
7
app/src/main/res/values/styleable.xml
Normal file
7
app/src/main/res/values/styleable.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="IntListPreference">
|
||||
<attr name="entries" format="reference" />
|
||||
<attr name="entryValues" format="reference" />
|
||||
</declare-styleable>
|
||||
</resources>
|
|
@ -179,7 +179,7 @@
|
|||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:focusable">true</item>
|
||||
<item name="android:theme">@style/Theme.MaterialComponents</item>
|
||||
<item name="android:theme">@style/Theme.MaterialComponents.DayNight</item>
|
||||
<item name="rippleColor">@color/selection_color</item>
|
||||
<item name="fontFamily">@font/inter_semibold</item>
|
||||
<item name="textAllCaps">false</item>
|
||||
|
|
|
@ -1,148 +1,150 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<PreferenceCategory
|
||||
android:layout="@layout/item_header"
|
||||
android:title="@string/setting_ui">
|
||||
<ListPreference
|
||||
android:entries="@array/entires_theme"
|
||||
android:entryValues="@array/values_theme"
|
||||
android:icon="@drawable/ic_day"
|
||||
android:title="@string/setting_theme"
|
||||
app:defaultValue="AUTO"
|
||||
app:key="KEY_THEME"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:layout="@layout/item_header"
|
||||
app:title="@string/setting_ui">
|
||||
|
||||
<org.oxycblt.auxio.settings.ui.IntListPreference
|
||||
app:defaultValue="@integer/theme_auto"
|
||||
app:entries="@array/entires_theme"
|
||||
app:entryValues="@array/values_theme"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_THEME2"
|
||||
app:icon="@drawable/ic_day"
|
||||
app:title="@string/setting_theme" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_accent"
|
||||
android:title="@string/setting_accent"
|
||||
app:allowDividerBelow="false"
|
||||
app:icon="@drawable/ic_accent"
|
||||
app:key="KEY_ACCENT2"
|
||||
app:summary="@string/setting_accent_unknown" />
|
||||
app:summary="@string/color_label_blue"
|
||||
app:title="@string/setting_accent" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:layout="@layout/item_header"
|
||||
android:title="@string/setting_display">
|
||||
app:layout="@layout/item_header"
|
||||
app:title="@string/setting_display">
|
||||
|
||||
<ListPreference
|
||||
android:icon="@drawable/ic_artist"
|
||||
android:title="@string/setting_lib_display"
|
||||
app:defaultValue="SHOW_ARTISTS"
|
||||
<org.oxycblt.auxio.settings.ui.IntListPreference
|
||||
app:defaultValue="@integer/display_artist"
|
||||
app:entries="@array/entries_lib_display"
|
||||
app:entryValues="@array/values_lib_display"
|
||||
app:key="KEY_LIBRARY_DISPLAY_MODE"
|
||||
app:icon="@drawable/ic_artist"
|
||||
app:key="KEY_LIB_MODE"
|
||||
app:title="@string/setting_lib_display"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_show_covers"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_SHOW_COVERS"
|
||||
app:summary="@string/setting_show_covers_desc" />
|
||||
app:summary="@string/setting_show_covers_desc"
|
||||
app:title="@string/setting_show_covers" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_quality_covers"
|
||||
app:defaultValue="false"
|
||||
app:dependency="KEY_SHOW_COVERS"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_QUALITY_COVERS"
|
||||
app:summary="@string/setting_quality_covers_desc" />
|
||||
app:summary="@string/setting_quality_covers_desc"
|
||||
app:title="@string/setting_quality_covers" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_color_notif"
|
||||
app:defaultValue="true"
|
||||
app:dependency="KEY_SHOW_COVERS"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_COLOR_NOTIF"
|
||||
app:summary="@string/setting_color_desc" />
|
||||
app:summary="@string/setting_color_desc"
|
||||
app:title="@string/setting_color_notif" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_use_alt_action"
|
||||
app:allowDividerBelow="false"
|
||||
app:defaultValue="false"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_ALT_NOTIF_ACTION"
|
||||
app:summaryOff="@string/setting_use_alt_loop"
|
||||
app:summaryOn="@string/setting_use_alt_shuffle" />
|
||||
app:summaryOn="@string/setting_use_alt_shuffle"
|
||||
app:title="@string/setting_use_alt_action" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:layout="@layout/item_header"
|
||||
android:title="@string/setting_audio">
|
||||
app:layout="@layout/item_header"
|
||||
app:title="@string/setting_audio">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_audio_focus"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_AUDIO_FOCUS"
|
||||
app:summary="@string/setting_audio_focus_desc" />
|
||||
app:summary="@string/setting_audio_focus_desc"
|
||||
app:title="@string/setting_audio_focus" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_audio_plug_mgt"
|
||||
app:allowDividerBelow="false"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_PLUG_MGT"
|
||||
app:summary="@string/setting_audio_plug_mgt_desc" />
|
||||
app:summary="@string/setting_audio_plug_mgt_desc"
|
||||
app:title="@string/setting_audio_plug_mgt" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:layout="@layout/item_header"
|
||||
android:title="@string/setting_behavior">
|
||||
app:layout="@layout/item_header"
|
||||
app:title="@string/setting_behavior">
|
||||
|
||||
<ListPreference
|
||||
android:title="@string/setting_behavior_song_playback_mode"
|
||||
app:defaultValue="ALL_SONGS"
|
||||
<org.oxycblt.auxio.settings.ui.IntListPreference
|
||||
app:defaultValue="@integer/play_mode_songs"
|
||||
app:entries="@array/entries_song_playback_mode"
|
||||
app:entryValues="@array/values_song_playback_mode"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_SONG_PLAY_MODE"
|
||||
app:key="KEY_SONG_PLAY_MODE2"
|
||||
app:title="@string/setting_behavior_song_playback_mode"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
android:title="@string/setting_behavior_at_end"
|
||||
app:defaultValue="LOOP_PAUSE"
|
||||
app:entries="@array/entries_at_end"
|
||||
app:entryValues="@array/values_at_end"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_AT_END"
|
||||
app:title="@string/setting_behavior_at_end"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:title="@string/setting_behavior_keep_shuffle"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_KEEP_SHUFFLE"
|
||||
app:summary="@string/setting_behavior_keep_shuffle_desc" />
|
||||
app:summary="@string/setting_behavior_keep_shuffle_desc"
|
||||
app:title="@string/setting_behavior_keep_shuffle" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/setting_behavior_rewind_prev"
|
||||
app:allowDividerBelow="false"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_PREV_REWIND"
|
||||
app:summary="@string/setting_behavior_rewind_prev_desc" />
|
||||
app:summary="@string/setting_behavior_rewind_prev_desc"
|
||||
app:title="@string/setting_behavior_rewind_prev" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:layout="@layout/item_header"
|
||||
android:title="@string/setting_content">
|
||||
app:layout="@layout/item_header"
|
||||
app:title="@string/setting_content">
|
||||
|
||||
<Preference
|
||||
android:title="@string/setting_content_save"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_SAVE_STATE"
|
||||
app:summary="@string/setting_content_save_desc" />
|
||||
app:summary="@string/setting_content_save_desc"
|
||||
app:title="@string/setting_content_save" />
|
||||
|
||||
<Preference
|
||||
android:title="@string/setting_content_blacklist"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_BLACKLIST"
|
||||
app:summary="@string/setting_content_blacklist_desc" />
|
||||
app:summary="@string/setting_content_blacklist_desc"
|
||||
app:title="@string/setting_content_blacklist" />
|
||||
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
|
@ -1,6 +1,6 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.4.31"
|
||||
ext.kotlin_version = "1.4.32"
|
||||
ext.navigation_version = "2.3.4"
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -29,6 +29,57 @@ When creating a ViewHolder for a `RecyclerView`, one should use `BaseViewHolder`
|
|||
|
||||
Data is often bound using Binding Adapters, which are XML attributes assigned in layout files that can automatically display data, usually written as `app:bindingAdapterName="@{data}"`. Its recommended to use these instead of duplicating code manually.
|
||||
|
||||
#### Integers
|
||||
|
||||
Integer representations of data/ui elements are used heavily in Auxio.
|
||||
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 | QueueSongViewHolder
|
||||
0xA006 | UserQueueHeaderViewHolder
|
||||
|
||||
0xA007 | AlbumHeaderViewHolder
|
||||
0xA008 | AlbumSongViewHolder
|
||||
0xA009 | ArtistHeaderViewHolder
|
||||
0xA00A | ArtistAlbumViewHolder
|
||||
0xA00B | GenreHeaderViewHolder
|
||||
0xA00C | GenreSongViewHolder
|
||||
|
||||
0xA0A0 | Auxio notification code
|
||||
0xA0C0 | Auxio request code
|
||||
|
||||
0xA1XX | Data Integer Space [Stored for IO efficency]
|
||||
|
||||
0xA100 | LoopMode.NONE
|
||||
0xA101 | LoopMode.ONCE
|
||||
0xA102 | LoopMode.INFINITE
|
||||
|
||||
0xA103 | PlaybackMode.IN_GENRE
|
||||
0xA104 | PlaybackMode.IN_ARTIST
|
||||
0xA105 | PlaybackMode.IN_ALBUM
|
||||
0xA106 | PlaybackMode.ALL_SONGS
|
||||
|
||||
0xA107 | DisplayMode.SHOW_ALL
|
||||
0xA108 | DisplayMode.SHOW_GENRES
|
||||
0xA109 | DisplayMode.SHOW_ARTISTS
|
||||
0xA10A | DisplayMode.SHOW_ALBUMS
|
||||
0xA10B | DisplayMode.SHOW_SONGS
|
||||
|
||||
0xA10C | SortMode.NONE
|
||||
0xA10D | SortMode.ALPHA_UP
|
||||
0xA10E | SortMode.ALPHA_DOWN
|
||||
0xA10F | SortMode.NUMERIC_UP
|
||||
0xA110 | SortMode.NUMERIC_DOWN
|
||||
```
|
||||
|
||||
#### Package structure overview
|
||||
|
||||
Auxio's package structure is mostly based around the features, and then any sub-features or components involved with that. There are some shared packages however. A diagram of the package structure is shown below:
|
||||
|
@ -51,7 +102,8 @@ org.oxycblt.auxio # Main UI's and logging utilities
|
|||
├──.search # Search UI
|
||||
├──.settings # Settings UI and systems
|
||||
│ ├──.blacklist # Excluded Directories UI/Systems
|
||||
│ └──.ui # Contains UI's related to the settings view, such as the about screen
|
||||
│ ├──.accent # Accent UI + Systems
|
||||
│ └──.ui # Settings-Related UIs
|
||||
├──.songs # Songs UI
|
||||
└──.ui # Shared user interface utilities
|
||||
```
|
||||
|
@ -120,7 +172,7 @@ Shared RecyclerView utilities, often for adapters and ViewHolders. Important one
|
|||
|
||||
#### `.settings`
|
||||
|
||||
The settings system is primarily based off of `SettingsManager`, a wrapper around `SharedPreferences`. This allows settings to be read/written in a much simpler/safer manner and without a context being needed. The Settings UI is largely contained in `SettingsListFragment`, while the `.ui` sub-package contains UIs related to the settings UI, such as the About Dialog.
|
||||
The settings system is primarily based off of `SettingsManager`, a wrapper around `SharedPreferences`. This allows settings to be read/written in a much simpler/safer manner and without a context being needed. The Settings UI is largely contained in `SettingsListFragment`, while the sub-packages contain sub-uis related to the `SettingsListFragment`, such as the custom list preference `IntListPreference` and the about dialog.
|
||||
|
||||
#### `.search`
|
||||
|
||||
|
|
Loading…
Reference in a new issue