WIP Prefs: ListPreference, autoupdate

This commit is contained in:
charlag 2022-05-21 22:33:13 +02:00
parent 8b8f39e13b
commit a07f2f20fc
No known key found for this signature in database
GPG Key ID: 5B96E7C76F0CA558
3 changed files with 188 additions and 43 deletions

View File

@ -22,6 +22,7 @@ import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.MainActivity
@ -30,10 +31,13 @@ import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.databinding.ActivityPreferencesBinding
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.settings.PrefStore
import com.keylesspalace.tusky.settings.get
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.getNonNullString
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.coroutines.launch
import javax.inject.Inject
class PreferencesActivity :
@ -47,6 +51,10 @@ class PreferencesActivity :
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
@Inject
lateinit var prefStore: PrefStore
private var restartActivitiesOnExit: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
@ -93,16 +101,49 @@ class PreferencesActivity :
}
restartActivitiesOnExit = intent.getBooleanExtra("restart", false)
lifecycleScope.launch {
// When certain preferences are changed we want to apply the changes
// TODO: Maybe this is not the best place to do it and we should trigger
// it from fragment?
var lastValue = prefStore.get()
prefStore.data.collect { newValue ->
if (lastValue.appTheme != newValue.appTheme) {
Log.d("activeTheme", newValue.appTheme)
ThemeUtils.setAppNightMode(newValue.appTheme)
restartActivitiesOnExit = true
restartCurrentActivity()
} else if (lastValue.statusTextSize != newValue.statusTextSize ||
lastValue.useAbsoluteTime != newValue.useAbsoluteTime ||
lastValue.showBotOverlay != newValue.showBotOverlay ||
lastValue.animateAvatars != newValue.animateAvatars ||
lastValue.useBlurhash != newValue.useBlurhash ||
lastValue.showCardsInTimelines != newValue.showCardsInTimelines ||
lastValue.confirmReblogs != newValue.confirmReblogs ||
lastValue.enableSwipeForTabs != newValue.enableSwipeForTabs ||
lastValue.mainNavPosition != newValue.mainNavPosition ||
lastValue.hideTopToolbar != newValue.hideTopToolbar
) {
restartActivitiesOnExit = true
} else if (lastValue.language != newValue.language) {
restartActivitiesOnExit = true
restartCurrentActivity()
}
lastValue = newValue
}
}
}
override fun onResume() {
super.onResume()
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this)
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this)
}
private fun saveInstanceState(outState: Bundle) {
@ -115,26 +156,7 @@ class PreferencesActivity :
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
"appTheme" -> {
val theme = sharedPreferences.getNonNullString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
Log.d("activeTheme", theme)
ThemeUtils.setAppNightMode(theme)
restartActivitiesOnExit = true
this.restartCurrentActivity()
}
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", "useBlurhash",
"showCardsInTimelines", "confirmReblogs", "confirmFavourites",
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR -> {
restartActivitiesOnExit = true
}
"language" -> {
restartActivitiesOnExit = true
this.restartCurrentActivity()
}
}
// FIXME
eventHub.dispatch(PreferenceChangedEvent(key))
}

View File

@ -25,10 +25,13 @@ import androidx.lifecycle.lifecycleScope
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.settings.AppTheme
import com.keylesspalace.tusky.settings.PrefData
import com.keylesspalace.tusky.settings.PrefStore
import com.keylesspalace.tusky.settings.getBlocking
import com.keylesspalace.tusky.settings.listPreference
import com.keylesspalace.tusky.settings.makePreferenceScreen
import com.keylesspalace.tusky.settings.named
import com.keylesspalace.tusky.settings.preferenceCategory
import com.keylesspalace.tusky.settings.switchPreference
import com.keylesspalace.tusky.util.ThemeUtils
@ -36,7 +39,9 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizePx
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import javax.inject.Inject
@ -53,6 +58,7 @@ class PreferencesFragment : Fragment(), Injectable {
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
private var updateTrigger: (() -> Unit)? = null
private fun updatePrefs(updater: (PrefData) -> PrefData) {
lifecycleScope.launch {
prefStore.updateData(updater)
@ -70,33 +76,73 @@ class PreferencesFragment : Fragment(), Injectable {
prefStore.data.collect {
prefs = it
// trigger update?
withContext(Dispatchers.Main) {
updateTrigger?.invoke()
}
}
}
makePreferenceScreen(view) {
this.updateTrigger = makePreferenceScreen(view) {
preferenceCategory(R.string.pref_title_appearance_settings) {
switchPreference(getString(R.string.pref_title_hide_top_toolbar), prefs::hideTopToolbar) {
val themeOptions = listOf(
AppTheme.NIGHT.value named R.string.app_them_dark,
AppTheme.DAY.value named R.string.app_theme_light,
AppTheme.BLACK.value named R.string.app_theme_black,
AppTheme.AUTO.value named R.string.app_theme_auto,
AppTheme.AUTO_SYSTEM.value named R.string.app_theme_system,
)
listPreference(
getString(R.string.pref_title_app_theme),
themeOptions,
{ prefs.appTheme }) {
updatePrefs { data -> data.copy(appTheme = it) }
}
switchPreference(
getString(R.string.pref_title_hide_top_toolbar),
{ prefs.hideTopToolbar }
) {
updatePrefs { data -> data.copy(hideTopToolbar = it) }
}
switchPreference(getString(R.string.pref_title_hide_follow_button), prefs::hideFab) {
switchPreference(
getString(R.string.pref_title_hide_follow_button),
{ prefs.hideFab }
) {
updatePrefs { data -> data.copy(hideFab = it) }
}
switchPreference(getString(R.string.pref_title_absolute_time), prefs::useAbsoluteTime) {
switchPreference(
getString(R.string.pref_title_absolute_time),
{ prefs.useAbsoluteTime }
) {
updatePrefs { data -> data.copy(useAbsoluteTime = it) }
}
switchPreference(getString(R.string.pref_title_bot_overlay), prefs::showBotOverlay) {
switchPreference(
getString(R.string.pref_title_bot_overlay),
{ prefs.showBotOverlay }
) {
updatePrefs { data -> data.copy(showBotOverlay = it) }
}
switchPreference(getString(R.string.pref_title_animate_gif_avatars), prefs::animateAvatars) {
switchPreference(
getString(R.string.pref_title_animate_gif_avatars),
{ prefs.animateAvatars }
) {
updatePrefs { data -> data.copy(animateAvatars = it) }
}
switchPreference(getString(R.string.pref_title_animate_custom_emojis), prefs::animateEmojis) {
switchPreference(
getString(R.string.pref_title_animate_custom_emojis),
{ prefs.animateEmojis }
) {
updatePrefs { data -> data.copy(animateEmojis = it) }
}
switchPreference(getString(R.string.pref_title_gradient_for_media), prefs::useBlurhash) {
switchPreference(
getString(R.string.pref_title_gradient_for_media),
{ prefs.useBlurhash }
) {
updatePrefs { data -> data.copy(useBlurhash = it) }
}
switchPreference(getString(R.string.pref_title_show_cards_in_timelines), prefs::showCardsInTimelines) {
switchPreference(
getString(R.string.pref_title_show_cards_in_timelines),
{ prefs.showCardsInTimelines }
) {
updatePrefs { data -> data.copy(showCardsInTimelines = it) }
}
}

View File

@ -11,16 +11,21 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.switchmaterial.SwitchMaterial
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.util.ThemeUtils
typealias Update = () -> Unit
class PreferenceParent(
val context: Context,
val addPref: (pref: View) -> Unit
)
val registerUpdate: (update: Update) -> Unit,
val addPref: (pref: View) -> Unit,
) {
}
//inline fun PreferenceParent.preference(builder: Preference.() -> Unit): Preference {
// val pref = Preference(context)
@ -103,6 +108,16 @@ fun PreferenceParent.checkBoxPreference(
addPref(layout)
}
private fun TextView.setTextAppearanceRef(ref: Int) {
val refs = TypedValue()
context.theme.resolveAttribute(ref, refs, true)
setTextAppearance(context, refs.resourceId)
}
private fun TextView.setTextColorRef(ref: Int) {
setTextColor(ThemeUtils.getColor(context, ref))
}
fun PreferenceParent.switchPreference(
title: String,
isChecked: () -> Boolean,
@ -111,12 +126,8 @@ fun PreferenceParent.switchPreference(
val layout = itemLayout(context)
val textView = TextView(context).apply {
text = title
val refs = TypedValue()
context.theme.resolveAttribute(android.R.attr.textAppearanceListItem, refs, true)
setTextAppearance(context, refs.resourceId)
setTextColor(ThemeUtils.getColor(context, android.R.attr.textColorPrimary))
// this is in resource but not at runtime?
// setSingleLine()
setTextAppearanceRef(android.R.attr.textAppearanceListItem)
setTextColorRef(android.R.attr.textColorPrimary)
ellipsize = TextUtils.TruncateAt.MARQUEE
}
textView.layoutParams = LinearLayout.LayoutParams(
@ -140,13 +151,70 @@ fun PreferenceParent.switchPreference(
layout.addView(switchLayout)
val switch = SwitchMaterial(context)
switch.isChecked = isChecked()
registerUpdate {
switch.isChecked = isChecked()
}
switch.setOnCheckedChangeListener { _, isChecked -> onSelection(isChecked) }
switchLayout.addView(switch)
addPref(layout)
}
data class PreferenceOption<T>(@StringRes val name: Int, val value: T)
infix fun <T> T.named(@StringRes name: Int) = PreferenceOption(name, this)
fun <T> PreferenceParent.listPreference(
title: String,
options: List<PreferenceOption<T>>,
selected: () -> T,
onSelection: (T) -> Unit,
) {
val layout = itemLayout(context).apply {
isClickable = true
val outValue = TypedValue()
context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
setBackgroundResource(outValue.resourceId)
setPadding(dpToPx(16), dpToPx(16), dpToPx(16), dpToPx(16))
}
val linearLayout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
}
val titleView = TextView(context).apply {
text = title
setTextAppearanceRef(android.R.attr.textAppearanceListItem)
setTextColorRef(android.R.attr.textColorPrimary)
}
linearLayout.addView(titleView)
val optionView = TextView(context)
linearLayout.addView(optionView)
layout.addView(linearLayout)
addPref(layout)
registerUpdate {
val selectedOptionIndex = options.indexOfFirst { it.value == selected() }
optionView.setText(options[selectedOptionIndex].name)
layout.setOnClickListener {
AlertDialog.Builder(context)
.setSingleChoiceItems(
options.map { context.getString(it.name) }.toTypedArray(),
selectedOptionIndex,
) { dialog, wh ->
onSelection(options[wh].value)
dialog.dismiss()
}
.setCancelable(true)
.show()
}
}
}
fun PreferenceParent.preferenceCategory(
@StringRes title: Int,
@ -175,19 +243,28 @@ fun PreferenceParent.preferenceCategory(
}
titleLayout.addView(titleView)
val newParent = PreferenceParent(context) { categoryLayout.addView(it) }
val newParent = PreferenceParent(context, registerUpdate) { categoryLayout.addView(it) }
builder(newParent)
}
inline fun Fragment.makePreferenceScreen(
viewGroup: ViewGroup,
builder: PreferenceParent.() -> Unit
) {
): (() -> Unit) {
val context = requireContext()
val parent = PreferenceParent(context) { viewGroup.addView(it) }
val updates = mutableListOf<Update>()
val updateTrigger = {
for (update in updates) {
update()
}
}
val parent = PreferenceParent(context, updates::add) { viewGroup.addView(it) }
// For some functions (like dependencies) it's much easier for us if we attach screen first
// and change it later
builder(parent)
// Run once to update all views
updateTrigger()
return updateTrigger
}
fun View.dpToPx(dp: Int) = Utils.dpToPx(this.context, dp)