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

Commit ff50c039 authored by Jacky Wang's avatar Jacky Wang
Browse files

[Catalyst] Enrich PreferenceLifecycleProvider

Bug: 332202168
Flag: EXEMPT library
Test: manual
Change-Id: Ie5939918242420ac6d007bf4e09f82e15f0654f5
parent 12883a2c
Loading
Loading
Loading
Loading
+53 −13
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.settingslib.metadata

import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.os.Bundle

/**
 * Interface to provide dynamic preference title.
@@ -82,25 +85,62 @@ interface PreferenceRestrictionProvider {
interface PreferenceLifecycleProvider {

    /**
     * Called when preference is attached to UI.
     * Callbacks of preference fragment `onCreate`.
     *
     * Subclass could override this API to register runtime condition listeners, and invoke
     * `onPreferenceStateChanged(this)` on the given [preferenceStateObserver] to update UI when
     * internal state (e.g. availability, enabled state, title, summary) is changed.
     * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal
     * state (e.g. availability, enabled state, title, summary) is changed.
     */
    fun onAttach(context: Context, preferenceStateObserver: PreferenceStateObserver)
    fun onCreate(context: PreferenceLifecycleContext) {}

    /**
     * Called when preference is detached from UI.
     * Callbacks of preference fragment `onStart`.
     *
     * Clean up and release resource.
     * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal
     * state (e.g. availability, enabled state, title, summary) is changed.
     */
    fun onDetach(context: Context)
    fun onStart(context: PreferenceLifecycleContext) {}

    /** Observer of preference state. */
    interface PreferenceStateObserver {
    /**
     * Callbacks of preference fragment `onResume`.
     *
     * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal
     * state (e.g. availability, enabled state, title, summary) is changed.
     */
    fun onResume(context: PreferenceLifecycleContext) {}

    /** Callbacks of preference fragment `onPause`. */
    fun onPause(context: PreferenceLifecycleContext) {}

    /** Callbacks of preference fragment `onStop`. */
    fun onStop(context: PreferenceLifecycleContext) {}

        /** Callbacks when preference state is changed. */
        fun onPreferenceStateChanged(preference: PreferenceMetadata)
    /** Callbacks of preference fragment `onDestroy`. */
    fun onDestroy(context: PreferenceLifecycleContext) {}

    /**
     * Receives the result from a previous call of
     * [PreferenceLifecycleContext.startActivityForResult].
     *
     * @return true if the result is handled
     */
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = false
}

/**
 * [Context] for preference lifecycle.
 *
 * A preference fragment is associated with a [PreferenceLifecycleContext] only.
 */
abstract class PreferenceLifecycleContext(context: Context) : ContextWrapper(context) {

    /** Notifies that preference state is changed and update preference widget UI. */
    abstract fun notifyPreferenceChange(preference: PreferenceMetadata)

    /**
     * Starts activity for result, see [android.app.Activity.startActivityForResult].
     *
     * This API can be invoked by any preference, the caller must ensure the request code is unique
     * on the preference screen.
     */
    abstract fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?)
}
+37 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.settingslib.preference

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.annotation.XmlRes
@@ -41,7 +42,7 @@ open class PreferenceFragment :
        createPreferenceScreen(PreferenceScreenFactory(this))

    override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? {
        preferenceScreenBindingHelper?.close()
        preferenceScreenBindingHelper?.onDestroy()
        preferenceScreenBindingHelper = null

        val context = factory.context
@@ -66,9 +67,11 @@ open class PreferenceFragment :
                    bindRecursively(it, preferenceBindingFactory, preferenceHierarchy)
                } ?: return null
            }

        preferenceScreenBindingHelper =
            PreferenceScreenBindingHelper(
                context,
                this,
                preferenceBindingFactory,
                preferenceScreen,
                preferenceHierarchy,
@@ -87,12 +90,44 @@ open class PreferenceFragment :
    override fun getPreferenceScreenBindingKey(context: Context): String? =
        arguments?.getString(EXTRA_BINDING_SCREEN_KEY)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        preferenceScreenBindingHelper?.onCreate()
    }

    override fun onStart() {
        super.onStart()
        preferenceScreenBindingHelper?.onStart()
    }

    override fun onResume() {
        super.onResume()
        preferenceScreenBindingHelper?.onResume()
    }

    override fun onPause() {
        preferenceScreenBindingHelper?.onPause()
        super.onPause()
    }

    override fun onStop() {
        preferenceScreenBindingHelper?.onStop()
        super.onStop()
    }

    override fun onDestroy() {
        preferenceScreenBindingHelper?.close()
        preferenceScreenBindingHelper?.onDestroy()
        preferenceScreenBindingHelper = null
        super.onDestroy()
    }

    @Suppress("DEPRECATION")
    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        preferenceScreenBindingHelper?.onActivityResult(requestCode, resultCode, data)
    }

    protected fun getPreferenceKeysInHierarchy(): Set<String> =
        preferenceScreenBindingHelper?.getPreferences()?.map { it.key }?.toSet() ?: setOf()

+62 −14
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.settingslib.preference

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.preference.Preference
@@ -29,6 +31,7 @@ import com.android.settingslib.datastore.KeyedObservable
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceHierarchy
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenRegistry
@@ -45,10 +48,11 @@ import java.util.concurrent.Executor
 */
class PreferenceScreenBindingHelper(
    context: Context,
    fragment: PreferenceFragment,
    private val preferenceBindingFactory: PreferenceBindingFactory,
    private val preferenceScreen: PreferenceScreen,
    private val preferenceHierarchy: PreferenceHierarchy,
) : KeyedDataObservable<String>(), AutoCloseable {
) : KeyedDataObservable<String>() {

    private val handler = Handler(Looper.getMainLooper())
    private val executor =
@@ -58,8 +62,22 @@ class PreferenceScreenBindingHelper(
            }
        }

    private val preferenceLifecycleContext =
        object : PreferenceLifecycleContext(context) {
            override fun notifyPreferenceChange(preference: PreferenceMetadata) =
                notifyChange(preference.key, CHANGE_REASON_STATE)

            @Suppress("DEPRECATION")
            override fun startActivityForResult(
                intent: Intent,
                requestCode: Int,
                options: Bundle?,
            ) = fragment.startActivityForResult(intent, requestCode, options)
        }

    private val preferences: ImmutableMap<String, PreferenceMetadata>
    private val dependencies: ImmutableMultimap<String, String>
    private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider>
    private val storages = mutableSetOf<KeyedObservable<String>>()

    private val preferenceObserver: KeyedObserver<String?>
@@ -71,16 +89,10 @@ class PreferenceScreenBindingHelper(
            }
        }

    private val stateObserver =
        object : PreferenceLifecycleProvider.PreferenceStateObserver {
            override fun onPreferenceStateChanged(preference: PreferenceMetadata) {
                notifyChange(preference.key, CHANGE_REASON_STATE)
            }
        }

    init {
        val preferencesBuilder = ImmutableMap.builder<String, PreferenceMetadata>()
        val dependenciesBuilder = ImmutableMultimap.builder<String, String>()
        val lifecycleAwarePreferences = mutableListOf<PreferenceLifecycleProvider>()
        fun PreferenceMetadata.addDependency(dependency: PreferenceMetadata) {
            dependenciesBuilder.put(key, dependency.key)
        }
@@ -88,7 +100,7 @@ class PreferenceScreenBindingHelper(
        fun PreferenceMetadata.add() {
            preferencesBuilder.put(key, this)
            dependencyOfEnabledState(context)?.addDependency(this)
            if (this is PreferenceLifecycleProvider) onAttach(context, stateObserver)
            if (this is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(this)
            if (this is PersistentPreference<*>) storages.add(storage(context))
        }

@@ -106,6 +118,7 @@ class PreferenceScreenBindingHelper(
        preferenceHierarchy.addPreferences()
        this.preferences = preferencesBuilder.buildOrThrow()
        this.dependencies = dependenciesBuilder.build()
        this.lifecycleAwarePreferences = lifecycleAwarePreferences.toTypedArray()

        preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) }
        addObserver(preferenceObserver, executor)
@@ -137,13 +150,48 @@ class PreferenceScreenBindingHelper(

    fun getPreferences() = preferenceHierarchy.getAllPreferences()

    override fun close() {
        removeObserver(preferenceObserver)
        val context = preferenceScreen.context
        for (preference in preferences.values) {
            if (preference is PreferenceLifecycleProvider) preference.onDetach(context)
    fun onCreate() {
        for (preference in lifecycleAwarePreferences) {
            preference.onCreate(preferenceLifecycleContext)
        }
    }

    fun onStart() {
        for (preference in lifecycleAwarePreferences) {
            preference.onStart(preferenceLifecycleContext)
        }
    }

    fun onResume() {
        for (preference in lifecycleAwarePreferences) {
            preference.onResume(preferenceLifecycleContext)
        }
    }

    fun onPause() {
        for (preference in lifecycleAwarePreferences) {
            preference.onPause(preferenceLifecycleContext)
        }
    }

    fun onStop() {
        for (preference in lifecycleAwarePreferences) {
            preference.onStop(preferenceLifecycleContext)
        }
    }

    fun onDestroy() {
        removeObserver(preferenceObserver)
        for (storage in storages) storage.removeObserver(storageObserver)
        for (preference in lifecycleAwarePreferences) {
            preference.onDestroy(preferenceLifecycleContext)
        }
    }

    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        for (preference in lifecycleAwarePreferences) {
            if (preference.onActivityResult(requestCode, resultCode, data)) break
        }
    }

    companion object {