From 0305eb0bebd0921767931a66c491d7f0843e8398 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sat, 3 Apr 2021 10:53:16 -0600 Subject: [PATCH] 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. --- app/build.gradle | 8 +- .../oxycblt/auxio/database/DatabaseUtils.kt | 2 - .../auxio/database/PlaybackStateDatabase.kt | 2 +- .../java/org/oxycblt/auxio/music/Models.kt | 2 +- .../oxycblt/auxio/playback/state/LoopMode.kt | 6 +- .../auxio/playback/state/PlaybackMode.kt | 8 +- .../playback/state/PlaybackStateManager.kt | 4 + .../playback/system/PlaybackNotification.kt | 2 +- .../org/oxycblt/auxio/recycler/DisplayMode.kt | 32 +++ .../org/oxycblt/auxio/recycler/SortMode.kt | 12 +- .../recycler/viewholders/ModelHolders.kt | 17 -- .../oxycblt/auxio/settings/SettingUtils.kt | 15 -- .../oxycblt/auxio/settings/SettingsCompat.kt | 175 ++++++++++++++++ .../auxio/settings/SettingsListFragment.kt | 45 ++--- .../oxycblt/auxio/settings/SettingsManager.kt | 186 ++++++------------ .../auxio/settings/accent/AccentDialog.kt | 1 + .../settings/blacklist/BlacklistDialog.kt | 4 + .../auxio/settings/ui/IntListPrefDialog.kt | 21 ++ .../auxio/settings/ui/IntListPreference.kt | 85 ++++++++ .../main/java/org/oxycblt/auxio/ui/Accent.kt | 2 +- app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values/arrays.xml | 32 +-- app/src/main/res/values/ints.xml | 15 ++ app/src/main/res/values/strings.xml | 1 - app/src/main/res/values/styleable.xml | 7 + app/src/main/res/values/styles.xml | 2 +- app/src/main/res/xml/prefs_main.xml | 108 +++++----- build.gradle | 2 +- info/ARCHITECTURE.md | 56 +++++- 29 files changed, 575 insertions(+), 280 deletions(-) create mode 100644 app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPrefDialog.kt create mode 100644 app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPreference.kt create mode 100644 app/src/main/res/values/ints.xml create mode 100644 app/src/main/res/values/styleable.xml diff --git a/app/build.gradle b/app/build.gradle index b894526ec..e3189e555 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt b/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt index 88f90a960..dc85de2cd 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt index 3118b3fbe..122cbd001 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt @@ -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" diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index 7e6228d1f..b208db8fc 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt index 335ee36b8..f4aeb9b3e 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/LoopMode.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt index 0aeae3ef5..91bd8ec01 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackMode.kt @@ -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] diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index f01787bd4..d589607e4 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt index cbb0f3763..7ec0d8701 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt index d8e6a1ba2..569a0e694 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt @@ -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 + } + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index 0824423a9..8b420b70c 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt index c63929d47..b849db605 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt @@ -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]. */ diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt index c898c46ab..9b4161f6c 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt new file mode 100644 index 000000000..9dc274493 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt @@ -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" +} diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index 745590e5a..fbcb3882c 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -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" - } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt index a25205155..8750154d1 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt index b8a33fb75..8efb79d36 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/accent/AccentDialog.kt @@ -80,6 +80,7 @@ class AccentDialog : LifecycleDialog() { } companion object { + const val TAG = "TAG_ACCENT_DIALOG" const val KEY_PENDING_ACCENT = "AXKEY_PEND_ACCENT" } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt index 8c1a3925c..2c30f52ee 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt @@ -156,4 +156,8 @@ class BlacklistDialog : LifecycleDialog() { private fun getRootPath(): String { return Environment.getExternalStorageDirectory().absolutePath } + + companion object { + const val TAG = "TAG_BLACKLIST_DIALOG" + } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPrefDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPrefDialog.kt new file mode 100644 index 000000000..932db37a3 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPrefDialog.kt @@ -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" + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPreference.kt b/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPreference.kt new file mode 100644 index 000000000..08e611feb --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/ui/IntListPreference.kt @@ -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 + 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 { + override fun provideSummary(preference: IntListPreference?): CharSequence { + val index = getValueIndex() + + if (index != -1) { + return entries[index] + } + + return context.getString(prefR.string.not_set) + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/ui/Accent.kt b/app/src/main/java/org/oxycblt/auxio/ui/Accent.kt index 0dd144f99..d9ea3fe7a 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/Accent.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/Accent.kt @@ -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. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4ec9312bc..c96779d4e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -62,7 +62,6 @@ Hell Dunkel Akzentfarbe - Unbekannte Akzentfarbe Anzeige Musikbibliothekitems @@ -97,7 +96,7 @@ Wiedergabezustand abspeichern Der aktuell Wiedergabezustand jetzt abspeichern Ausgeschlossene Ordner - Die Inhalte der ausgeschlossene Ordner sind versteckt von deinen Musikbibliothek + Die Inhalte der ausgeschlossene Ordner sind versteckt von deiner Musikbibliothek Keine Musik gefunden diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 68bcdbd56..3bea9376c 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -6,11 +6,11 @@ @string/setting_theme_night - - AUTO - LIGHT - DARK - + + @integer/theme_auto + @integer/theme_light + @integer/theme_dark + @string/label_genres @@ -18,11 +18,11 @@ @string/label_albums - - SHOW_GENRES - SHOW_ARTISTS - SHOW_ALBUMS - + + @integer/display_genre + @integer/display_artist + @integer/display_album + @string/label_play_all_songs @@ -31,11 +31,11 @@ @string/label_play_genre - - ALL_SONGS - IN_ARTIST - IN_ALBUM - IN_GENRE + + @integer/play_mode_songs + @integer/play_mode_artist + @integer/play_mode_album + @integer/play_mode_genre @@ -44,7 +44,7 @@ @string/setting_behavior_end_stop - + LOOP_PAUSE LOOP STOP diff --git a/app/src/main/res/values/ints.xml b/app/src/main/res/values/ints.xml new file mode 100644 index 000000000..8db3e5c81 --- /dev/null +++ b/app/src/main/res/values/ints.xml @@ -0,0 +1,15 @@ + + + -1 + 1 + 2 + + 0xA108 + 0xA109 + 0xA10A + + 0xA103 + 0xA104 + 0xA105 + 0xA106 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 906af00f2..6af8fc9fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,7 +63,6 @@ Light Dark Accent - Unknown Accent Display Library Items diff --git a/app/src/main/res/values/styleable.xml b/app/src/main/res/values/styleable.xml new file mode 100644 index 000000000..21bbd907d --- /dev/null +++ b/app/src/main/res/values/styleable.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 363157101..110af2f0f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -179,7 +179,7 @@ wrap_content true true - @style/Theme.MaterialComponents + @style/Theme.MaterialComponents.DayNight @color/selection_color @font/inter_semibold false diff --git a/app/src/main/res/xml/prefs_main.xml b/app/src/main/res/xml/prefs_main.xml index 43c571e53..61f052ef1 100644 --- a/app/src/main/res/xml/prefs_main.xml +++ b/app/src/main/res/xml/prefs_main.xml @@ -1,148 +1,150 @@ - + - + app:layout="@layout/item_header" + app:title="@string/setting_ui"> + + + app:summary="@string/color_label_blue" + app:title="@string/setting_accent" /> + + app:layout="@layout/item_header" + app:title="@string/setting_display"> - + app:summary="@string/setting_show_covers_desc" + app:title="@string/setting_show_covers" /> + app:summary="@string/setting_quality_covers_desc" + app:title="@string/setting_quality_covers" /> + app:summary="@string/setting_color_desc" + app:title="@string/setting_color_notif" /> + app:summaryOn="@string/setting_use_alt_shuffle" + app:title="@string/setting_use_alt_action" /> + app:layout="@layout/item_header" + app:title="@string/setting_audio"> + app:summary="@string/setting_audio_focus_desc" + app:title="@string/setting_audio_focus" /> + app:summary="@string/setting_audio_plug_mgt_desc" + app:title="@string/setting_audio_plug_mgt" /> + app:layout="@layout/item_header" + app:title="@string/setting_behavior"> - + app:summary="@string/setting_behavior_keep_shuffle_desc" + app:title="@string/setting_behavior_keep_shuffle" /> + app:summary="@string/setting_behavior_rewind_prev_desc" + app:title="@string/setting_behavior_rewind_prev" /> + app:layout="@layout/item_header" + app:title="@string/setting_content"> + app:summary="@string/setting_content_save_desc" + app:title="@string/setting_content_save" /> + app:summary="@string/setting_content_blacklist_desc" + app:title="@string/setting_content_blacklist" /> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 70ac08e23..4724ca81e 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/info/ARCHITECTURE.md b/info/ARCHITECTURE.md index c5eaab868..50a17641f 100644 --- a/info/ARCHITECTURE.md +++ b/info/ARCHITECTURE.md @@ -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`