Developer quick setting for theming

Closes https://github.com/SchildiChat/SchildiChat-android/issues/117

Change-Id: Ie8fab9b7842e695b9e59a65d2cbf09c7edfd1dd8
This commit is contained in:
SpiritCroc 2022-05-29 14:19:48 +02:00
parent 226428e0ba
commit 3e58f2c254
9 changed files with 299 additions and 47 deletions

View File

@ -0,0 +1,62 @@
package de.spiritcroc.menu
import android.content.res.Resources
import android.view.Menu
import android.view.MenuItem
import android.view.View
object ArrayOptionsMenuHelper {
private data class OptionsMenuMetadata(
val menuId: Int,
val entriesId: Int,
val valuesId: Int,
val values: HashMap<Int, String> = HashMap(),
var action: (String) -> Boolean = { false }
)
private val menuLookup = HashMap<Int, OptionsMenuMetadata>()
fun createSubmenu(resources: Resources,
menuItem: MenuItem,
groupId: Int,
entriesId: Int,
valuesId: Int,
initialValue: String,
sortFunction: (List<Pair<String, String>>) -> List<Pair<String, String>> = { it },
action: (String) -> Boolean) {
val menuId = menuItem.itemId
var metadata = menuLookup[menuId]
if (metadata == null || metadata.entriesId != entriesId || metadata.valuesId != valuesId) {
metadata = OptionsMenuMetadata(menuId, entriesId, valuesId)
menuLookup[menuId] = metadata
}
metadata.action = action
menuItem.subMenu.apply {
clear()
val themeEntries = resources.getStringArray(entriesId)
val themeValues = resources.getStringArray(valuesId)
sortFunction(themeEntries.zip(themeValues)).forEach { theme ->
val itemId = View.generateViewId()
val item = add(groupId, itemId, Menu.NONE, theme.first).apply {
isCheckable = true
}
metadata.values[itemId] = theme.second
if (initialValue == theme.second) {
item.isChecked = true
}
}
}
}
fun handleSubmenu(item: MenuItem, vararg menuIds: Int): Boolean {
menuIds.forEach { menuId ->
val metadata = menuLookup[menuId] ?: return@forEach
val value = metadata.values[item.itemId] ?: return@forEach
if (metadata.action(value)) {
return true
}
}
return false
}
}

View File

@ -35,12 +35,14 @@ import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import de.spiritcroc.menu.ArrayOptionsMenuHelper
import im.vector.app.AppStateHandler
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.extensions.restart
import im.vector.app.core.extensions.validateBackPressed
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.pushers.PushersManager
@ -536,6 +538,38 @@ class HomeActivity :
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
menu.findItem(R.id.menu_home_init_sync_legacy)?.isVisible = vectorPreferences.developerMode()
menu.findItem(R.id.menu_home_init_sync_optimized)?.isVisible = vectorPreferences.developerMode()
menu.findItem(R.id.dev_theming)?.isVisible = vectorPreferences.developerMode()
// Base theme setting
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_base_theme),
R.id.dev_base_theme_group,
R.array.theme_entries,
R.array.theme_values,
ThemeUtils.getCurrentActiveTheme(this)
) { value ->
ThemeUtils.setCurrentActiveTheme(this, value)
restart()
true
}
// Accent color theme setting
val isLightTheme = ThemeUtils.isLightTheme(this)
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_theme_accent),
R.id.dev_theme_accent_group,
if (isLightTheme) R.array.sc_accent_color_light_entries else R.array.sc_accent_color_dark_entries,
if (isLightTheme) R.array.sc_accent_color_light_values else R.array.sc_accent_color_dark_values,
ThemeUtils.getCurrentActiveThemeAccent(this),
sortFunction = { list -> list.sortedBy { it.first } }
) { value ->
ThemeUtils.setCurrentActiveThemeAccent(this, value)
restart()
true
}
return super.onPrepareOptionsMenu(menu)
}
@ -575,7 +609,10 @@ class HomeActivity :
}
}
return super.onOptionsItemSelected(item)
return ArrayOptionsMenuHelper.handleSubmenu(item,
R.id.dev_base_theme,
R.id.dev_theme_accent
) || super.onOptionsItemSelected(item)
}
override fun onBackPressed() {

View File

@ -73,6 +73,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.vanniktech.emoji.EmojiPopup
import de.spiritcroc.matrixsdk.util.DbgUtil
import de.spiritcroc.matrixsdk.util.Dimber
import de.spiritcroc.menu.ArrayOptionsMenuHelper
import de.spiritcroc.recyclerview.StickyHeaderItemDecoration
import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager
import de.spiritcroc.recyclerview.widget.LinearLayoutManager
@ -85,6 +86,7 @@ import im.vector.app.core.epoxy.LayoutManagerStateRestorer
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.restart
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.showKeyboard
import im.vector.app.core.extensions.trackItemsVisibilityChange
@ -1165,18 +1167,68 @@ class TimelineFragment @Inject constructor(
updateMenuThreadNotificationBadge(menu, state)
}
// Selected bubble style
val selectedBubbleStyle = when (bubbleThemeUtils.getBubbleStyle()) {
BubbleThemeUtils.BUBBLE_STYLE_NONE -> R.id.dev_bubble_style_none
BubbleThemeUtils.BUBBLE_STYLE_START -> R.id.dev_bubble_style_start
BubbleThemeUtils.BUBBLE_STYLE_BOTH -> R.id.dev_bubble_style_both
BubbleThemeUtils.BUBBLE_STYLE_ELEMENT -> R.id.dev_bubble_style_element
else -> R.id.dev_bubble_style_both
}
menu.findItem(selectedBubbleStyle).isChecked = true
// Hidden events
menu.findItem(R.id.dev_hidden_events).isChecked = vectorPreferences.shouldShowHiddenEvents()
// Bubble style
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_bubble_style),
R.id.dev_bubble_style_group,
R.array.bubble_style_entries,
R.array.bubble_style_values,
bubbleThemeUtils.getBubbleStyle()
) { value ->
if (value != bubbleThemeUtils.getBubbleStyle()) {
bubbleThemeUtils.setBubbleStyle(value)
requireActivity().restart()
}
true
}
// Base theme setting
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_base_theme),
R.id.dev_base_theme_group,
R.array.theme_entries,
R.array.theme_values,
ThemeUtils.getCurrentActiveTheme(requireContext())
) { value ->
ThemeUtils.setCurrentActiveTheme(requireContext(), value)
requireActivity().restart()
true
}
// Accent color theme setting
val isLightTheme = ThemeUtils.isLightTheme(requireContext())
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_theme_accent),
R.id.dev_theme_accent_group,
if (isLightTheme) R.array.sc_accent_color_light_entries else R.array.sc_accent_color_dark_entries,
if (isLightTheme) R.array.sc_accent_color_light_values else R.array.sc_accent_color_dark_values,
ThemeUtils.getCurrentActiveThemeAccent(requireContext()),
sortFunction = { list -> list.sortedBy { it.first } }
) { value ->
ThemeUtils.setCurrentActiveThemeAccent(requireContext(), value)
requireActivity().restart()
true
}
// Bubble corner radius
ArrayOptionsMenuHelper.createSubmenu(
resources,
menu.findItem(R.id.dev_bubble_rounded_corners),
R.id.dev_bubble_rounded_corners_group,
R.array.bubble_appearance_roundness_entries,
R.array.bubble_appearance_roundness_values,
bubbleThemeUtils.getBubbleRoundnessSetting()
) { value ->
bubbleThemeUtils.setBubbleRoundnessSetting(value)
reloadTimeline()
true
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -1209,22 +1261,6 @@ class TimelineFragment @Inject constructor(
navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
true
}
R.id.dev_bubble_style_none -> {
handleSetBubbleStyle(BubbleThemeUtils.BUBBLE_STYLE_NONE)
true
}
R.id.dev_bubble_style_start -> {
handleSetBubbleStyle(BubbleThemeUtils.BUBBLE_STYLE_START)
true
}
R.id.dev_bubble_style_both -> {
handleSetBubbleStyle(BubbleThemeUtils.BUBBLE_STYLE_BOTH)
true
}
R.id.dev_bubble_style_element -> {
handleSetBubbleStyle(BubbleThemeUtils.BUBBLE_STYLE_ELEMENT)
true
}
R.id.dev_hidden_events -> {
val shouldShow = !item.isChecked
vectorPreferences.setShouldShowHiddenEvents(shouldShow)
@ -1263,7 +1299,12 @@ class TimelineFragment @Inject constructor(
}
true
}
else -> super.onOptionsItemSelected(item)
else -> ArrayOptionsMenuHelper.handleSubmenu(item,
R.id.dev_base_theme,
R.id.dev_bubble_style,
R.id.dev_theme_accent,
R.id.dev_bubble_rounded_corners
) || super.onOptionsItemSelected(item)
}
}
@ -2851,13 +2892,6 @@ class TimelineFragment @Inject constructor(
}
}
private fun handleSetBubbleStyle(bubbleStyle: String) {
if (bubbleStyle != bubbleThemeUtils.getBubbleStyle()) {
bubbleThemeUtils.setBubbleStyle(bubbleStyle)
requireActivity().recreate()
}
}
// AttachmentsHelper.Callback
override fun onContentAttachmentsReady(attachments: List<ContentAttachmentData>) {
val grouped = attachments.toGroupedContentAttachmentData()

View File

@ -785,6 +785,7 @@ class TimelineViewModel @AssistedInject constructor(
R.id.show_participants -> true // SC
R.id.dev_bubble_style, // SC
R.id.dev_hidden_events, // SC
R.id.dev_theming, // SC
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
}

View File

@ -72,9 +72,18 @@ class BubbleThemeUtils @Inject constructor(private val context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(BUBBLE_STYLE_KEY, value).apply()
}
fun getBubbleRoundnessSetting(): String {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(BUBBLE_ROUNDNESS_KEY, BUBBLE_ROUNDNESS_DEFAULT) ?: BUBBLE_ROUNDNESS_DEFAULT
}
fun setBubbleRoundnessSetting(value: String) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(BUBBLE_ROUNDNESS_KEY, value).apply()
}
fun getBubbleAppearance(): ScBubbleAppearance {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val baseAppearance = when (prefs.getString(BUBBLE_ROUNDNESS_KEY, BUBBLE_ROUNDNESS_DEFAULT)) {
val baseAppearance = when (getBubbleRoundnessSetting()) {
BUBBLE_ROUNDNESS_R1 -> r1ScBubbleAppearance
BUBBLE_ROUNDNESS_R2 -> r2ScBubbleAppearance
else -> defaultScBubbleAppearance

View File

@ -273,6 +273,52 @@ object ThemeUtils {
mColorByAttr.clear()
}
// For developer quick options
fun setCurrentActiveTheme(context: Context, theme: String) {
var darkTheme = getApplicationDarkTheme(context)
var lightTheme = getApplicationLightTheme(context)
val darkAccent = getApplicationDarkThemeAccent(context)
val lightAccent = getApplicationLightThemeAccent(context)
if (useDarkTheme(context)) {
darkTheme = theme
} else {
lightTheme = theme
}
setApplicationTheme(context, lightTheme, darkTheme, lightAccent, darkAccent)
}
// For developer quick options
fun getCurrentActiveTheme(context: Context): String {
return if (useDarkTheme(context)) {
getApplicationDarkTheme(context)
} else {
getApplicationLightTheme(context)
}
}
// For developer quick options
fun setCurrentActiveThemeAccent(context: Context, accent: String) {
val darkTheme = getApplicationDarkTheme(context)
val lightTheme = getApplicationLightTheme(context)
var darkAccent = getApplicationDarkThemeAccent(context)
var lightAccent = getApplicationLightThemeAccent(context)
if (useDarkTheme(context)) {
darkAccent = accent
} else {
lightAccent = accent
}
setApplicationTheme(context, lightTheme, darkTheme, lightAccent, darkAccent)
}
// For developer quick options
fun getCurrentActiveThemeAccent(context: Context): String {
return if (useDarkTheme(context)) {
getApplicationDarkThemeAccent(context)
} else {
getApplicationLightThemeAccent(context)
}
}
fun setApplicationLightTheme(context: Context, theme: String) {
setApplicationTheme(context, theme, getApplicationDarkTheme(context),
getApplicationLightThemeAccent(context), getApplicationDarkThemeAccent(context))

View File

@ -38,4 +38,36 @@
app:iconTint="?vctr_content_secondary"
app:showAsAction="always" />
<item
android:id="@+id/dev_theming"
android:title="@string/settings_theme"
android:visible="false"
tools:visible="true"
app:showAsAction="never">
<menu>
<group>
<item
android:id="@+id/dev_base_theme"
android:title="@string/settings_theme">
<!-- Programmatically inflated menu -->
<menu>
<group
android:id="@+id/dev_base_theme_group"
android:checkableBehavior="single" />
</menu>
</item>
<item
android:id="@+id/dev_theme_accent"
android:title="@string/setting_sc_accent_color">
<!-- Programmatically inflated menu -->
<menu>
<group
android:id="@+id/dev_theme_accent_group"
android:checkableBehavior="single" />
</menu>
</item>
</group>
</menu>
</item>
</menu>

View File

@ -90,22 +90,52 @@
android:visible="false"
app:showAsAction="never"
tools:visible="true">
<!-- Programmatically inflated menu -->
<menu>
<group
android:checkableBehavior="single">
android:id="@+id/dev_bubble_style_group"
android:checkableBehavior="single" />
</menu>
</item>
<item
android:id="@+id/dev_theming"
android:title="@string/settings_theme"
android:visible="false"
tools:visible="true"
app:showAsAction="never">
<menu>
<group>
<item
android:id="@+id/dev_bubble_style_both"
android:title="@string/bubble_style_both"
android:checked="true" />
android:id="@+id/dev_base_theme"
android:title="@string/settings_theme">
<!-- Programmatically inflated menu -->
<menu>
<group
android:id="@+id/dev_base_theme_group"
android:checkableBehavior="single" />
</menu>
</item>
<item
android:id="@+id/dev_bubble_style_start"
android:title="@string/bubble_style_start" />
android:id="@+id/dev_theme_accent"
android:title="@string/setting_sc_accent_color">
<!-- Programmatically inflated menu -->
<menu>
<group
android:id="@+id/dev_theme_accent_group"
android:checkableBehavior="single" />
</menu>
</item>
<item
android:id="@+id/dev_bubble_style_element"
android:title="@string/bubble_style_element" />
<item
android:id="@+id/dev_bubble_style_none"
android:title="@string/bubble_style_none" />
android:id="@+id/dev_bubble_rounded_corners"
android:title="@string/bubble_rounded_corners">
<!-- Programmatically inflated menu -->
<menu>
<group
android:id="@+id/dev_bubble_rounded_corners_group"
android:checkableBehavior="single" />
</menu>
</item>
</group>
</menu>
</item>

View File

@ -93,6 +93,7 @@
<string name="settings_advanced_theme_settings">Advanced theme settings</string>
<string name="settings_user_colors">User colors</string>
<string name="settings_theme_colors">Theme colors</string>
<string name="setting_sc_accent_color">Accent color</string>
<string name="setting_sc_accent_color_light">Accent color for light themes</string>
<string name="setting_sc_accent_color_dark">Accent color for dark themes</string>
<string name="settings_sc_accent_disclaimer">Note that these color settings only apply to SC themes, and not Element themes.</string>