diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 037aeb2f2..356c3efc6 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -7,14 +7,12 @@ import android.os.Bundle import android.util.Log import android.view.View import android.view.WindowInsets -import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.databinding.DataBindingUtil import org.oxycblt.auxio.databinding.ActivityMainBinding import org.oxycblt.auxio.playback.PlaybackService import org.oxycblt.auxio.settings.SettingsManager -import org.oxycblt.auxio.settings.SettingsViewModel import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.handleTransparentSystemBars import org.oxycblt.auxio.ui.toColor @@ -24,8 +22,6 @@ import org.oxycblt.auxio.ui.toColor // TODO: Landscape UI layouts // FIXME: Compat issue with Versions 5 that leads to progress bar looking off class MainActivity : AppCompatActivity() { - private val settingsModel: SettingsViewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -37,42 +33,16 @@ class MainActivity : AppCompatActivity() { val settingsManager = SettingsManager.init(applicationContext) - AppCompatDelegate.setDefaultNightMode( - settingsManager.getTheme() - ) + AppCompatDelegate.setDefaultNightMode(settingsManager.theme) - accent = settingsManager.getAccent() + accent = settingsManager.accent // Apply the theme setTheme(accent.second) - if (settingsManager.getEdgeToEdge() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + if (settingsManager.edgeEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { doEdgeToEdgeSetup(binding) } - - // --- VIEWMODEL SETUP --- - - settingsModel.theme.observe(this) { - if (it != null) { - doThemeRecreate(it) - } - } - - settingsModel.accent.observe(this) { - if (it != null) { - recreate() - - settingsModel.doneWithAccentUpdate() - } - } - - settingsModel.edge.observe(this) { - if (it != null) { - recreate() - - settingsModel.doneWithEdgeUpdate() - } - } } override fun onStart() { @@ -88,8 +58,7 @@ class MainActivity : AppCompatActivity() { window?.apply { statusBarColor = Color.TRANSPARENT - // Use a heavily transparent scrim on the nav bar as otherwise the transparency wont - // work. + // Use a heavily transparent scrim on the nav bar as full transparency is borked navigationBarColor = R.color.nav_color.toColor(this@MainActivity) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt index 8b5ead606..7976778d1 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt @@ -39,12 +39,13 @@ class LibraryViewModel : ViewModel() { private var mSearchHasFocus = false val searchHasFocus: Boolean get() = mSearchHasFocus - init { - val prefsManager = SettingsManager.getInstance() + private val settingsManager = SettingsManager.getInstance() + init { + // The SortMode isn't really that urgent, so throw it into a coroutine because I can viewModelScope.launch { mSortMode.value = withContext(Dispatchers.IO) { - prefsManager.getLibrarySortMode() + settingsManager.librarySortMode } } } @@ -133,13 +134,7 @@ class LibraryViewModel : ViewModel() { if (mode != mSortMode.value) { mSortMode.value = mode - viewModelScope.launch { - withContext(Dispatchers.IO) { - val prefsManager = SettingsManager.getInstance() - - prefsManager.setLibrarySortMode(mSortMode.value!!) - } - } + settingsManager.librarySortMode = mode } } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt new file mode 100644 index 000000000..6a648b394 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingUtils.kt @@ -0,0 +1,30 @@ +package org.oxycblt.auxio.settings + +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. + */ +fun String.toThemeInt(): Int { + return when (this) { + SettingsManager.EntryNames.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + SettingsManager.EntryNames.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO + SettingsManager.EntryNames.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES + + else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } +} + +@DrawableRes +fun Int.toThemeIcon(): Int { + return when (this) { + AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto + AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day + AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night + + else -> R.drawable.ic_auto + } +} 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 854cbd3e7..8439b67b9 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -6,9 +6,10 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.appcompat.app.AppCompatDelegate -import androidx.fragment.app.activityViewModels import androidx.preference.Preference +import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat +import androidx.preference.children import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.afollestad.materialdialogs.MaterialDialog @@ -20,56 +21,13 @@ import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.getDetailedAccentSummary class SettingsListFragment : PreferenceFragmentCompat() { - private val settingsModel: SettingsViewModel by activityViewModels() - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // --- PREFERENCE ITEM SETUP --- - val themePref = findPreference(SettingsManager.Keys.KEY_THEME)?.apply { - setIcon( - when (AppCompatDelegate.getDefaultNightMode()) { - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto - AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day - AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night - - else -> R.drawable.ic_auto - } - ) - } - - val accentPref = findPreference(SettingsManager.Keys.KEY_ACCENT)?.apply { - onPreferenceClickListener = Preference.OnPreferenceClickListener { - showAccentDialog() - true - } - - summary = getDetailedAccentSummary(requireActivity(), accent) - } - - // --- VIEWMODEL SETUP --- - - settingsModel.theme.observe(viewLifecycleOwner) { - if (it != null) { - themePref?.setIcon( - when (it) { - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto - AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day - AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night - - else -> R.drawable.ic_auto - } - ) - - settingsModel.doneWithThemeUpdate() - } - } - - settingsModel.accent.observe(viewLifecycleOwner) { - if (it != null) { - accentPref?.summary = getDetailedAccentSummary(requireActivity(), it) - } + preferenceScreen.children.forEach { + recursivelyHandleChildren(it) } Log.d(this::class.simpleName, "Fragment created.") @@ -79,6 +37,51 @@ class SettingsListFragment : PreferenceFragmentCompat() { setPreferencesFromResource(R.xml.prefs_main, rootKey) } + private fun recursivelyHandleChildren(pref: Preference) { + if (pref is PreferenceCategory) { + pref.children.forEach { + recursivelyHandleChildren(it) + } + } else { + handlePreference(pref) + } + } + + private fun handlePreference(it: Preference) { + it.apply { + when (it.key) { + SettingsManager.Keys.KEY_THEME -> { + setIcon(AppCompatDelegate.getDefaultNightMode().toThemeIcon()) + + onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, value -> + AppCompatDelegate.setDefaultNightMode((value as String).toThemeInt()) + + setIcon(AppCompatDelegate.getDefaultNightMode().toThemeIcon()) + + true + } + } + + SettingsManager.Keys.KEY_ACCENT -> { + onPreferenceClickListener = Preference.OnPreferenceClickListener { + showAccentDialog() + true + } + + summary = getDetailedAccentSummary(requireActivity(), accent) + } + + SettingsManager.Keys.KEY_EDGE_TO_EDGE -> { + onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ -> + requireActivity().recreate() + + true + } + } + } + } + } + private fun showAccentDialog() { MaterialDialog(requireActivity()).show { title(R.string.setting_accent) @@ -89,7 +92,9 @@ class SettingsListFragment : PreferenceFragmentCompat() { val recycler = RecyclerView(requireContext()).apply { adapter = AccentAdapter { if (it.first != accent.first) { - SettingsManager.getInstance().setAccent(it) + SettingsManager.getInstance().accent = it + + requireActivity().recreate() } this@show.dismiss() 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 8641d34bd..0b02af3b1 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -19,6 +19,51 @@ class SettingsManager private constructor(context: Context) : sharedPrefs.registerOnSharedPreferenceChangeListener(this) } + val theme: Int + get() { + return sharedPrefs.getString(Keys.KEY_THEME, EntryNames.THEME_AUTO)?.toThemeInt() + ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + } + + var accent: Pair + get() { + val accentIndex = sharedPrefs.getInt(Keys.KEY_ACCENT, 5) + + // Accent is stored as an index [to be efficient], so retrieve it when done. + return ACCENTS[accentIndex] + } + set(value) { + val accentIndex = ACCENTS.indexOf(value) + + check(accentIndex != -1) { "Invalid accent" } + + sharedPrefs.edit() + .putInt(Keys.KEY_ACCENT, accentIndex) + .apply() + } + + val edgeEnabled: Boolean + get() { + return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false) + } + + var librarySortMode: SortMode + get() { + return SortMode.fromInt( + sharedPrefs.getInt( + Keys.KEY_LIBRARY_SORT_MODE, + SortMode.CONSTANT_ALPHA_DOWN + ) + ) ?: SortMode.ALPHA_DOWN + } + set(value) { + sharedPrefs.edit() + .putInt(Keys.KEY_LIBRARY_SORT_MODE, value.toInt()) + .apply() + } + + // --- CALLBACKS --- + private val callbacks = mutableListOf() fun addCallback(callback: Callback) { @@ -29,74 +74,9 @@ class SettingsManager private constructor(context: Context) : callbacks.remove(callback) } - fun getTheme(): Int { - // Turn the string from SharedPreferences into an actual theme value that can - // be used, as apparently the preference system provided by androidx doesn't like integers - // for some...reason. - return when (sharedPrefs.getString(Keys.KEY_THEME, Theme.THEME_AUTO)) { - Theme.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - Theme.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO - Theme.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES - - else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - } - } - - fun getAccent(): Pair { - val accentIndex = sharedPrefs.getInt(Keys.KEY_ACCENT, 5) - - return ACCENTS[accentIndex] - } - - fun setAccent(accent: Pair) { - val accentIndex = ACCENTS.indexOf(accent) - - check(accentIndex != -1) { "Invalid accent" } - - sharedPrefs.edit() - .putInt(Keys.KEY_ACCENT, accentIndex) - .apply() - - callbacks.forEach { - it.onAccentUpdate(getAccent()) - } - } - - fun getEdgeToEdge(): Boolean { - return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false) - } - - fun setLibrarySortMode(sortMode: SortMode) { - sharedPrefs.edit() - .putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toInt()) - .apply() - } - - fun getLibrarySortMode(): SortMode { - return SortMode.fromInt( - sharedPrefs.getInt( - Keys.KEY_LIBRARY_SORT_MODE, - SortMode.CONSTANT_ALPHA_DOWN - ) - ) ?: SortMode.ALPHA_DOWN - } - // --- OVERRIDES --- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - when (key) { - Keys.KEY_THEME -> { - callbacks.forEach { - it.onThemeUpdate(getTheme()) - } - } - - Keys.KEY_EDGE_TO_EDGE -> { - callbacks.forEach { - it.onEdgeToEdgeUpdate(getEdgeToEdge()) - } - } - } } companion object { @@ -135,7 +115,7 @@ class SettingsManager private constructor(context: Context) : const val KEY_EDGE_TO_EDGE = "KEY_EDGE" } - private object Theme { + object EntryNames { const val THEME_AUTO = "AUTO" const val THEME_LIGHT = "LIGHT" const val THEME_DARK = "DARK" @@ -145,9 +125,5 @@ class SettingsManager private constructor(context: Context) : * An interface for receiving some settings updates. * [SharedPreferences.OnSharedPreferenceChangeListener]. */ - interface Callback { - fun onThemeUpdate(newTheme: Int) {} - fun onAccentUpdate(newAccent: Pair) {} - fun onEdgeToEdgeUpdate(isEdgeToEdge: Boolean) {} - } + interface Callback } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsViewModel.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsViewModel.kt deleted file mode 100644 index 7f6f9828f..000000000 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsViewModel.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.oxycblt.auxio.settings - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -/** - * A [ViewModel] that provides a better interface for observing settings updates compared to - * [SettingsManager.Callback] or the default SharedPreferences listener. - * // TODO: Roll values & updates into this viewmodel - * @author OxygenCobalt - */ -class SettingsViewModel : ViewModel(), SettingsManager.Callback { - private val mTheme = MutableLiveData() - val theme: LiveData get() = mTheme - - private val mAccent = MutableLiveData?>() - val accent: LiveData?> get() = mAccent - - private val mEdge = MutableLiveData() - val edge: LiveData = mEdge - - private val settingsManager = SettingsManager.getInstance() - - init { - settingsManager.addCallback(this) - } - - fun doneWithThemeUpdate() { - mTheme.value = null - } - - fun doneWithAccentUpdate() { - mAccent.value = null - } - - fun doneWithEdgeUpdate() { - mEdge.value = null - } - - override fun onThemeUpdate(newTheme: Int) { - mTheme.value = newTheme - } - - override fun onAccentUpdate(newAccent: Pair) { - mAccent.value = newAccent - } - - override fun onEdgeToEdgeUpdate(isEdgeToEdge: Boolean) { - mEdge.value = isEdgeToEdge - } - - override fun onCleared() { - super.onCleared() - - settingsManager.removeCallback(this) - } -} diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index b2acd40a0..43481953f 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -27,6 +27,7 @@ diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index 06ed1c9ca..bf513f8e3 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -27,6 +27,7 @@ diff --git a/app/src/main/res/layout/fragment_genre_detail.xml b/app/src/main/res/layout/fragment_genre_detail.xml index e4d9485b7..3a6c71a36 100644 --- a/app/src/main/res/layout/fragment_genre_detail.xml +++ b/app/src/main/res/layout/fragment_genre_detail.xml @@ -27,6 +27,7 @@ diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 432012cbf..8cdae18a3 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -14,6 +14,8 @@ android:id="@+id/library_toolbar" style="@style/Toolbar.Style" android:theme="@style/Toolbar.Style.Search" + android:background="?android:attr/windowBackground" + android:elevation="@dimen/elevation_normal" app:menu="@menu/menu_library" app:title="@string/label_library" /> diff --git a/app/src/main/res/layout/fragment_queue.xml b/app/src/main/res/layout/fragment_queue.xml index 3d23708dd..be856ecae 100644 --- a/app/src/main/res/layout/fragment_queue.xml +++ b/app/src/main/res/layout/fragment_queue.xml @@ -14,6 +14,7 @@ diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index f6380f428..32933cc59 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -10,6 +10,7 @@ diff --git a/app/src/main/res/layout/fragment_songs.xml b/app/src/main/res/layout/fragment_songs.xml index c5a6c39b0..5ef771d12 100644 --- a/app/src/main/res/layout/fragment_songs.xml +++ b/app/src/main/res/layout/fragment_songs.xml @@ -13,6 +13,7 @@ @color/selection_color - + - +