Loading packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt +53 −13 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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?) } packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt +37 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -66,9 +67,11 @@ open class PreferenceFragment : bindRecursively(it, preferenceBindingFactory, preferenceHierarchy) } ?: return null } preferenceScreenBindingHelper = PreferenceScreenBindingHelper( context, this, preferenceBindingFactory, preferenceScreen, preferenceHierarchy, Loading @@ -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() Loading packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +62 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 = Loading @@ -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?> Loading @@ -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) } Loading @@ -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)) } Loading @@ -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) Loading Loading @@ -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 { Loading Loading
packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt +53 −13 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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?) }
packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt +37 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -66,9 +67,11 @@ open class PreferenceFragment : bindRecursively(it, preferenceBindingFactory, preferenceHierarchy) } ?: return null } preferenceScreenBindingHelper = PreferenceScreenBindingHelper( context, this, preferenceBindingFactory, preferenceScreen, preferenceHierarchy, Loading @@ -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() Loading
packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +62 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 = Loading @@ -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?> Loading @@ -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) } Loading @@ -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)) } Loading @@ -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) Loading Loading @@ -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 { Loading