ui: re-add accent customization >api 31
Re-add accent customization on Android 12 and above. Previously, I disabled accent customization since I thought they were more or less useless with the new Material You dynamic colors system. Turns out I severely underestimated how horribly OEMs would botch the dynamic colors system. Guess I was blinded by my adherence to the pixel line. Re-add the accent customization for those who do not have a good dynamic color palette at all. Resolves #131.
This commit is contained in:
parent
eb293022e8
commit
e1e032d254
12 changed files with 87 additions and 68 deletions
|
@ -9,8 +9,10 @@
|
|||
- About screen now shows counts for multiple types of library items, alongside a total duration
|
||||
- New disc, track, song count, and duration sorting modes
|
||||
|
||||
### What's Improved
|
||||
#### What's Improved
|
||||
- Re-enabled theme customization on Android 12
|
||||
- The tab selector now hides itself when there is only one tab
|
||||
- Added more buttons to the smallest widget form
|
||||
|
||||
#### What's Fixed
|
||||
- Fixed incorrect ellipsizing on song items
|
||||
|
|
|
@ -108,26 +108,24 @@ class MainActivity : AppCompatActivity() {
|
|||
private fun setupTheme() {
|
||||
val settingsManager = SettingsManager.getInstance()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Android 12, let dynamic colors be our accent and only enable the black theme option
|
||||
if (isNight && settingsManager.useBlackTheme) {
|
||||
logD("Applying black theme [dynamic colors]")
|
||||
setTheme(R.style.Theme_Auxio_Black)
|
||||
}
|
||||
} else {
|
||||
// Below android 12, load the accent and enable theme customization
|
||||
// Disable theme customization above Android 12, as it's far enough in as a version to
|
||||
// the point where most phones should have an automatic option for light/dark theming.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
AppCompatDelegate.setDefaultNightMode(settingsManager.theme)
|
||||
val accent = settingsManager.accent
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
|
||||
// The black theme has a completely separate set of styles since style attributes cannot
|
||||
// be modified at runtime.
|
||||
if (isNight && settingsManager.useBlackTheme) {
|
||||
logD("Applying black theme [accent $accent]")
|
||||
setTheme(accent.blackTheme)
|
||||
} else {
|
||||
logD("Applying normal theme [accent $accent]")
|
||||
setTheme(accent.theme)
|
||||
}
|
||||
val accent = settingsManager.accent
|
||||
|
||||
// The black theme has a completely separate set of styles since style attributes cannot
|
||||
// be modified at runtime.
|
||||
if (isNight && settingsManager.useBlackTheme) {
|
||||
logD("Applying black theme [accent $accent]")
|
||||
setTheme(accent.blackTheme)
|
||||
} else {
|
||||
logD("Applying normal theme [accent $accent]")
|
||||
setTheme(accent.theme)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import androidx.core.content.edit
|
||||
import org.oxycblt.auxio.ui.accent.Accent
|
||||
|
||||
|
@ -43,16 +44,33 @@ fun handleAccentCompat(prefs: SharedPreferences): Accent {
|
|||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_ACCENT, accent)
|
||||
putInt(OldKeys.KEY_ACCENT3, accent)
|
||||
remove(OldKeys.KEY_ACCENT2)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
if (prefs.contains(OldKeys.KEY_ACCENT3)) {
|
||||
var accent = prefs.getInt(OldKeys.KEY_ACCENT3, 5)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Accents were previously frozen as soon as the OS was updated to android twelve,
|
||||
// as dynamic colors were enabled by default. This is no longer the case, so we need
|
||||
// to re-update the setting to dynamic colors here.
|
||||
accent = 16
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putInt(SettingsManager.KEY_ACCENT, accent)
|
||||
remove(OldKeys.KEY_ACCENT3)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
return Accent(prefs.getInt(SettingsManager.KEY_ACCENT, 5))
|
||||
}
|
||||
|
||||
/** Cache of the old keys used in Auxio. */
|
||||
private object OldKeys {
|
||||
const val KEY_ACCENT2 = "KEY_ACCENT2"
|
||||
const val KEY_ACCENT3 = "auxio_accent"
|
||||
}
|
||||
|
|
|
@ -279,7 +279,7 @@ class SettingsManager private constructor(context: Context) :
|
|||
// with auxio_.
|
||||
const val KEY_THEME = "KEY_THEME2"
|
||||
const val KEY_BLACK_THEME = "KEY_BLACK_THEME"
|
||||
const val KEY_ACCENT = "auxio_accent"
|
||||
const val KEY_ACCENT = "auxio_accent2"
|
||||
|
||||
const val KEY_LIB_TABS = "auxio_lib_tabs"
|
||||
const val KEY_SHOW_COVERS = "KEY_SHOW_COVERS"
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
|
||||
package org.oxycblt.auxio.ui.accent
|
||||
|
||||
import android.os.Build
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
val ACCENT_COUNT: Int
|
||||
get() = ACCENT_NAMES.size
|
||||
|
||||
private val ACCENT_NAMES =
|
||||
arrayOf(
|
||||
intArrayOf(
|
||||
R.string.clr_red,
|
||||
R.string.clr_pink,
|
||||
R.string.clr_purple,
|
||||
|
@ -40,10 +41,10 @@ private val ACCENT_NAMES =
|
|||
R.string.clr_orange,
|
||||
R.string.clr_brown,
|
||||
R.string.clr_grey,
|
||||
)
|
||||
R.string.clr_dynamic)
|
||||
|
||||
private val ACCENT_THEMES =
|
||||
arrayOf(
|
||||
intArrayOf(
|
||||
R.style.Theme_Auxio_Red,
|
||||
R.style.Theme_Auxio_Pink,
|
||||
R.style.Theme_Auxio_Purple,
|
||||
|
@ -60,10 +61,11 @@ private val ACCENT_THEMES =
|
|||
R.style.Theme_Auxio_Orange,
|
||||
R.style.Theme_Auxio_Brown,
|
||||
R.style.Theme_Auxio_Grey,
|
||||
)
|
||||
R.style.Theme_Auxio_App // Dynamic colors are on the base theme
|
||||
)
|
||||
|
||||
private val ACCENT_BLACK_THEMES =
|
||||
arrayOf(
|
||||
intArrayOf(
|
||||
R.style.Theme_Auxio_Black_Red,
|
||||
R.style.Theme_Auxio_Black_Pink,
|
||||
R.style.Theme_Auxio_Black_Purple,
|
||||
|
@ -80,10 +82,11 @@ private val ACCENT_BLACK_THEMES =
|
|||
R.style.Theme_Auxio_Black_Orange,
|
||||
R.style.Theme_Auxio_Black_Brown,
|
||||
R.style.Theme_Auxio_Black_Grey,
|
||||
)
|
||||
R.style.Theme_Auxio_Black // Dynamic colors are on the base theme
|
||||
)
|
||||
|
||||
private val ACCENT_PRIMARY_COLORS =
|
||||
arrayOf(
|
||||
intArrayOf(
|
||||
R.color.red_primary,
|
||||
R.color.pink_primary,
|
||||
R.color.purple_primary,
|
||||
|
@ -100,7 +103,7 @@ private val ACCENT_PRIMARY_COLORS =
|
|||
R.color.orange_primary,
|
||||
R.color.brown_primary,
|
||||
R.color.grey_primary,
|
||||
)
|
||||
R.color.dynamic_primary)
|
||||
|
||||
/**
|
||||
* The data object for an accent. In the UI this is known as a "Color Scheme." This can be nominally
|
||||
|
@ -122,4 +125,14 @@ data class Accent(val index: Int) {
|
|||
get() = ACCENT_BLACK_THEMES[index]
|
||||
val primary: Int
|
||||
get() = ACCENT_PRIMARY_COLORS[index]
|
||||
|
||||
companion object {
|
||||
val MAX =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
ACCENT_THEMES.size
|
||||
} else {
|
||||
// Disable the option for a dynamic accent
|
||||
ACCENT_THEMES.size - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ class AccentAdapter(listener: Listener) :
|
|||
|
||||
class AccentData : BackingData<Accent>() {
|
||||
override fun getItem(position: Int) = Accent(position)
|
||||
override fun getItemCount() = ACCENT_COUNT
|
||||
override fun getItemCount() = Accent.MAX
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,54 +30,47 @@ import org.oxycblt.auxio.util.newMainIntent
|
|||
* The default widget is displayed whenever there is no music playing. It just shows the message "No
|
||||
* music playing".
|
||||
*/
|
||||
fun createDefaultWidget(context: Context): RemoteViews {
|
||||
return createViews(context, R.layout.widget_default)
|
||||
}
|
||||
fun createDefaultWidget(context: Context) = createViews(context, R.layout.widget_default)
|
||||
|
||||
/**
|
||||
* The tiny widget is for an edge-case situation where a widget falls under the size class of the
|
||||
* small widget, either via landscape mode or exceptionally small screens.
|
||||
*/
|
||||
fun createTinyWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews {
|
||||
return createViews(context, R.layout.widget_tiny)
|
||||
fun createTinyWidget(context: Context, state: WidgetComponent.WidgetState) =
|
||||
createViews(context, R.layout.widget_tiny)
|
||||
.applyMeta(context, state)
|
||||
.applyBasicControls(context, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* The small widget is for 2x2 widgets and just shows the cover art and playback controls. This is
|
||||
* generally because a Medium widget is too large for this widget size and a text-only widget is too
|
||||
* small for this widget size.
|
||||
*/
|
||||
fun createSmallWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews {
|
||||
return createViews(context, R.layout.widget_small)
|
||||
fun createSmallWidget(context: Context, state: WidgetComponent.WidgetState) =
|
||||
createViews(context, R.layout.widget_small)
|
||||
.applyCover(context, state)
|
||||
.applyBasicControls(context, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* The medium widget is for 2x3 widgets and shows the cover art, title/artist, and three controls.
|
||||
* This is the default widget configuration.
|
||||
*/
|
||||
fun createMediumWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews {
|
||||
return createViews(context, R.layout.widget_medium)
|
||||
fun createMediumWidget(context: Context, state: WidgetComponent.WidgetState) =
|
||||
createViews(context, R.layout.widget_medium)
|
||||
.applyMeta(context, state)
|
||||
.applyBasicControls(context, state)
|
||||
}
|
||||
|
||||
/** The wide widget is for Nx2 widgets and is like the small widget but with more controls. */
|
||||
fun createWideWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews {
|
||||
return createViews(context, R.layout.widget_wide)
|
||||
fun createWideWidget(context: Context, state: WidgetComponent.WidgetState) =
|
||||
createViews(context, R.layout.widget_wide)
|
||||
.applyCover(context, state)
|
||||
.applyFullControls(context, state)
|
||||
}
|
||||
|
||||
/** The large widget is for 3x4 widgets and shows all metadata and controls. */
|
||||
fun createLargeWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews {
|
||||
return createViews(context, R.layout.widget_large)
|
||||
fun createLargeWidget(context: Context, state: WidgetComponent.WidgetState): RemoteViews =
|
||||
createViews(context, R.layout.widget_large)
|
||||
.applyMeta(context, state)
|
||||
.applyFullControls(context, state)
|
||||
}
|
||||
|
||||
private fun createViews(context: Context, @LayoutRes layout: Int): RemoteViews {
|
||||
val views = RemoteViews(context.packageName, layout)
|
||||
|
@ -114,7 +107,7 @@ private fun RemoteViews.applyCover(
|
|||
return this
|
||||
}
|
||||
|
||||
private fun RemoteViews.applyPlayControls(
|
||||
private fun RemoteViews.applyBasicControls(
|
||||
context: Context,
|
||||
state: WidgetComponent.WidgetState
|
||||
): RemoteViews {
|
||||
|
@ -129,15 +122,6 @@ private fun RemoteViews.applyPlayControls(
|
|||
R.drawable.ic_play
|
||||
})
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun RemoteViews.applyBasicControls(
|
||||
context: Context,
|
||||
state: WidgetComponent.WidgetState
|
||||
): RemoteViews {
|
||||
applyPlayControls(context, state)
|
||||
|
||||
setOnClickPendingIntent(
|
||||
R.id.widget_skip_prev, context.newBroadcastIntent(PlaybackService.ACTION_SKIP_PREV))
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
<android.widget.ImageView
|
||||
android:id="@+id/widget_cover"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
android:layout_width="@dimen/size_cover_mid_large"
|
||||
android:layout_height="@dimen/size_cover_mid_large"
|
||||
android:layout_marginEnd="@dimen/spacing_medium"
|
||||
android:contentDescription="@string/desc_no_cover"
|
||||
android:scaleType="centerCrop"
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
<!-- Note: Not actually transparent, making it transparent would actually make it translucent -->
|
||||
<color name="chrome_transparent">#01151515</color>
|
||||
|
||||
<color name="widget_surface">@color/material_dynamic_secondary20</color>
|
||||
<color name="widget_surface_inverse">@color/material_dynamic_neutral90</color>
|
||||
<color name="widget_on_surface_inverse">@color/material_dynamic_neutral20</color>
|
||||
|
||||
<color name="red_primary">#FFB4A8</color>
|
||||
<color name="red_on_primary">#680001</color>
|
||||
<color name="red_primary_container">#940002</color>
|
||||
|
@ -387,7 +391,5 @@
|
|||
<color name="grey_surface_inverse">#fafafa</color>
|
||||
<color name="grey_on_surface_inverse">#2D3132</color>
|
||||
|
||||
<color name="widget_surface">@color/material_dynamic_secondary20</color>
|
||||
<color name="widget_surface_inverse">@color/material_dynamic_neutral90</color>
|
||||
<color name="widget_on_surface_inverse">@color/material_dynamic_neutral20</color>
|
||||
<color name="dynamic_primary">@color/m3_ref_palette_dynamic_primary80</color>
|
||||
</resources>
|
|
@ -5,6 +5,10 @@
|
|||
<color name="chrome_translucent">#80000000</color>
|
||||
<color name="remote_translucent">#80ffffff</color>
|
||||
|
||||
<color name="widget_surface">@color/material_dynamic_primary95</color>
|
||||
<color name="widget_surface_inverse">@color/material_dynamic_neutral80</color>
|
||||
<color name="widget_on_surface_inverse">@color/material_dynamic_neutral95</color>
|
||||
|
||||
<color name="red_primary">#BC1714</color>
|
||||
<color name="red_on_primary">#FFFFFF</color>
|
||||
<color name="red_primary_container">#FFDAD3</color>
|
||||
|
@ -389,7 +393,5 @@
|
|||
<color name="grey_surface_inverse">#1f1f1f</color>
|
||||
<color name="grey_on_surface_inverse">#F0F0F0</color>
|
||||
|
||||
<color name="widget_surface">@color/material_dynamic_primary95</color>
|
||||
<color name="widget_surface_inverse">@color/material_dynamic_neutral80</color>
|
||||
<color name="widget_on_surface_inverse">@color/material_dynamic_neutral95</color>
|
||||
<color name="dynamic_primary">@color/m3_ref_palette_dynamic_primary40</color>
|
||||
</resources>
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<dimen name="size_cover_compact">48dp</dimen>
|
||||
<dimen name="size_cover_normal">56dp</dimen>
|
||||
<dimen name="size_cover_mid_large">88dp</dimen>
|
||||
<dimen name="size_cover_large">128dp</dimen>
|
||||
<dimen name="size_cover_mid_huge">192dp</dimen>
|
||||
<dimen name="size_cover_huge">256dp</dimen>
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_accent"
|
||||
app:isPreferenceVisible="@bool/enable_theme_settings"
|
||||
app:key="auxio_accent"
|
||||
app:key="auxio_accent2"
|
||||
app:title="@string/set_accent" />
|
||||
|
||||
<org.oxycblt.auxio.settings.pref.M3SwitchPreference
|
||||
|
|
Loading…
Reference in a new issue