WIP Prefs: ListPreference, autoupdate
This commit is contained in:
parent
8b8f39e13b
commit
a07f2f20fc
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue