Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to observe any / multiple preferences at once #12

Closed
MFlisar opened this issue Mar 15, 2021 · 4 comments
Closed

Ability to observe any / multiple preferences at once #12

MFlisar opened this issue Mar 15, 2021 · 4 comments

Comments

@MFlisar
Copy link

MFlisar commented Mar 15, 2021

I would like to be able to observe any key or only a bunch of keys inside a preference file - any plans on adding this?

Something like following:

flowSharedPreferences
    .asFlow("darkTheme", "mainColor", "accentColor") // list of keys
    .onEach { 
        print(it)
     }
    .launchIn(scope)

// or following
flowSharedPreferences
    .asFlowAny() // all preference changes will be emitted here
    .onEach { 
        print(it)
     }
    .launchIn(scope)

The flow should emit a custom class that also holds the key, e.g. a simple sealed class...

@tfcporciuncula
Copy link
Owner

tfcporciuncula commented Mar 15, 2021

This would only work if the types are know and the same for all preferences. SharedPreferences.OnSharedPreferenceChangeListener only emits the key of the preference that has changed, we still need to know what method to call to retrieve the updated value (e.g. getBoolean() or getFloat()). Because of this limitation, and because of how the library currently plays every nicely when it comes to types, I don't see this as an interesting addition.

Your first scenario can be achieved by combining different preferences, though, like this:

val darkThemePref = flowSharedPreferences.getBoolean("darkTheme")
val mainColorPref = flowSharedPreferences.getInt("mainColor")
val accentColorPref = flowSharedPreferences.getInt("accentColor")

combine(darkThemePref.asFlow(), mainColorPref.asFlow(), accentColorPref.asFlow()) { 
  darkTheme, mainColor, accentColor -> // ... 
}

And to satisfy your second example, you could create your own extension based on the implementation we have here that could look like something like this:

fun SharedPreferences.asFlowAny() =
  callbackFlow {
    val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> offerCatching(key) }
    registerOnSharedPreferenceChangeListener(listener)
    awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
  }
    .onStart { emit("first load trigger") }
    .map { it to getString(it, "") } // assuming all preferences you have are string
    .conflate()

// https://github.com/Kotlin/kotlinx.coroutines/issues/974
private fun <E> SendChannel<E>.offerCatching(element: E): Boolean {
  return runCatching { offer(element) }.getOrDefault(false)
}

The types must be the same in order for this to work, though, and that's a big limitation.

@MFlisar
Copy link
Author

MFlisar commented Mar 16, 2021

Thanks for the detailed response - I've set something up on my side that works as I want it now.

Just one thing: The OnSharedPreferenceChangeListener is generic as well and does not provide type safety nor does it know which type of data was changed - you could at least consider observing changes by observing keys (no values, the observer is responsible to correctly handle the keys... I must say that I mostly only observe preferences to recreate or update activities/fragments views and mostly, I do not optimise this. This means I simply observe a list of keys and call recreate in an activity so that the UI uses the new preferences (theme, colors, sizes, styles, ...)

@tfcporciuncula
Copy link
Owner

Yeah that's fair, what you're asking is a simple translation of OnSharedPreferenceChangeListener to a flow. I could turn my keyFlow into a public extension on top of SharedPreferences so consumers could use it directly if they want. I'll experiment a bit here and will get back to you soon.

@tfcporciuncula
Copy link
Owner

The new API was added on 1.4.0, thanks for the idea!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants