Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 249a1591 authored by Jacky Wang's avatar Jacky Wang
Browse files

[Catalyst] Notify entry point when screen state is updated

Most screens have entry point from another screen. When the screen
is updated, its entry point will get notified if dependencies are
managed well with observer/listener. However, observer/listener
mechanism may not be available across screens. This change provides an
enhancement to always notify entry point when screen state is updated.

Bug: 332201912
Flag: EXEMPT library
Test: manual
Change-Id: Ib714420b2498593ca52ac8b387cd2d3dac679d87
parent c009172c
Loading
Loading
Loading
Loading
+29 −3
Original line number Diff line number Diff line
@@ -103,7 +103,17 @@ class PreferenceScreenBindingHelper(
    private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider>
    private val observables = mutableMapOf<String, KeyedObservable<String>>()

    private val preferenceObserver: KeyedObserver<String?>
    /** Observer to update UI on preference change. */
    private val preferenceObserver =
        KeyedObserver<String?> { key, reason -> onPreferenceChange(key, reason) }

    /** Observer to update UI for screen entry points when it is updated across screens. */
    private val screenEntryPointObserver =
        KeyedObserver<String?> { key, reason ->
            // Current preference screen must not be notified to avoid infinite loop.
            // Rewrite the reason to STATE because this change comes from different screen.
            if (key != preferenceScreen.key) onPreferenceChange(key, PreferenceChangeReason.STATE)
        }

    private val observer =
        KeyedObserver<String> { key, reason ->
@@ -147,8 +157,9 @@ class PreferenceScreenBindingHelper(
        this.lifecycleAwarePreferences = lifecycleAwarePreferences.toTypedArray()

        val executor = HandlerExecutor.main
        preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
        addObserver(preferenceObserver, executor)
        // register observer to update other screen entry points on current screen
        screenEntryPointObservable.addObserver(screenEntryPointObserver, executor)

        preferenceScreen.forEachRecursively {
            val key = it.key ?: return@forEachRecursively
@@ -176,7 +187,11 @@ class PreferenceScreenBindingHelper(
        preferenceScreen.findPreference<Preference>(key)?.let {
            val node = preferences[key] ?: return@let
            preferenceBindingFactory.bind(it, node)
            if (it == preferenceScreen) fragment.updateActivityTitle()
            if (it == preferenceScreen) {
                fragment.updateActivityTitle()
                // Current screen is updated, notify to update entry point on previous screen
                screenEntryPointObservable.notifyChange(key, reason)
            }
        }

        // check reason to avoid potential infinite loop
@@ -229,6 +244,7 @@ class PreferenceScreenBindingHelper(

    fun onDestroy() {
        removeObserver(preferenceObserver)
        screenEntryPointObservable.removeObserver(screenEntryPointObserver)
        for ((key, observable) in observables) observable.removeObserver(key, observer)
        for (preference in lifecycleAwarePreferences) {
            preference.onDestroy(preferenceLifecycleContext)
@@ -244,6 +260,16 @@ class PreferenceScreenBindingHelper(
    companion object {
        private const val TAG = "MetadataBindingHelper"

        /**
         * A global [KeyedObservable] to notify UI rebinding for screen entry points. This field
         * needs to be static as screens could be shown by different activities.
         *
         * Ideally the screen metadata should add observers to notify change and then this logic is
         * redundant. However, observer mechanism may not be available across screens, so introduce
         * this enhancement to avoid potential UI consistency issue.
         */
        private val screenEntryPointObservable: KeyedObservable<String> = KeyedDataObservable()

        /** Updates preference screen that has incomplete hierarchy. */
        @JvmStatic
        fun bind(preferenceScreen: PreferenceScreen) {