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

Commit e511546f authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Import SettingsLib/Preference library" into main

parents b84b0870 7ce08629
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
package {
    default_applicable_licenses: ["frameworks_base_license"],
}

filegroup {
    name: "SettingsLibPreference-srcs",
    srcs: ["src/**/*.kt"],
}

android_library {
    name: "SettingsLibPreference",
    defaults: [
        "SettingsLintDefaults",
    ],
    srcs: [":SettingsLibPreference-srcs"],
    static_libs: [
        "SettingsLibDataStore",
        "SettingsLibMetadata",
        "androidx.annotation_annotation",
        "androidx.preference_preference",
    ],
    kotlincflags: ["-Xjvm-default=all"],
}
+6 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.settingslib.preference">

  <uses-sdk android:minSdkVersion="21" />
</manifest>
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.preference

import android.content.Context
import androidx.preference.DialogPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.preference.SeekBarPreference
import com.android.settingslib.metadata.DiscreteIntValue
import com.android.settingslib.metadata.DiscreteValue
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.RangeValue

/** Binding of preference widget and preference metadata. */
interface PreferenceBinding {

    /**
     * Provides a new [Preference] widget instance.
     *
     * By default, it returns a new [Preference] object. Subclass could override this method to
     * provide customized widget and do **one-off** initialization (e.g.
     * [Preference.setOnPreferenceClickListener]). To update widget everytime when state is changed,
     * override the [bind] method.
     *
     * Notes:
     * - DO NOT set any properties defined in [PreferenceMetadata]. For example,
     *   title/summary/icon/extras/isEnabled/isVisible/isPersistent/dependency. These properties
     *   will be reset by [bind].
     * - Override [bind] if needed to provide more information for customized widget.
     */
    fun createWidget(context: Context): Preference = Preference(context)

    /**
     * Binds preference widget with given metadata.
     *
     * Whenever metadata state is changed, this callback is invoked to update widget. By default,
     * the common states like title, summary, enabled, etc. are already applied. Subclass should
     * override this method to bind more data (e.g. read preference value from storage and apply it
     * to widget).
     *
     * @param preference preference widget created by [createWidget]
     * @param metadata metadata to apply
     */
    fun bind(preference: Preference, metadata: PreferenceMetadata) {
        metadata.apply {
            preference.key = key
            if (icon != 0) {
                preference.setIcon(icon)
            } else {
                preference.icon = null
            }
            val context = preference.context
            preference.peekExtras()?.clear()
            extras(context)?.let { preference.extras.putAll(it) }
            preference.title = getPreferenceTitle(context)
            preference.summary = getPreferenceSummary(context)
            preference.isEnabled = isEnabled(context)
            preference.isVisible =
                (this as? PreferenceAvailabilityProvider)?.isAvailable(context) != false
            preference.isPersistent = isPersistent(context)
            metadata.order(context)?.let { preference.order = it }
            // PreferenceRegistry will notify dependency change, so we do not need to set
            // dependency here. This simplifies dependency management and avoid the
            // IllegalStateException when call Preference.setDependency
            preference.dependency = null
            if (preference !is PreferenceScreen) { // avoid recursive loop when build graph
                preference.fragment = (this as? PreferenceScreenCreator)?.fragmentClass()?.name
                preference.intent = intent(context)
            }
            if (preference is DialogPreference) {
                preference.dialogTitle = preference.title
            }
            if (preference is ListPreference && this is DiscreteValue<*>) {
                preference.setEntries(valuesDescription)
                if (this is DiscreteIntValue) {
                    val intValues = context.resources.getIntArray(values)
                    preference.entryValues = Array(intValues.size) { intValues[it].toString() }
                } else {
                    preference.setEntryValues(values)
                }
            } else if (preference is SeekBarPreference && this is RangeValue) {
                preference.min = minValue
                preference.max = maxValue
                preference.seekBarIncrement = incrementStep
            }
        }
    }
}

/** Abstract preference screen to provide preference hierarchy and binding factory. */
interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenProvider {

    val preferenceBindingFactory: PreferenceBindingFactory
        get() = DefaultPreferenceBindingFactory

    override fun createPreferenceScreen(factory: PreferenceScreenFactory) =
        factory.getOrCreatePreferenceScreen().apply {
            inflatePreferenceHierarchy(preferenceBindingFactory, getPreferenceHierarchy(context))
        }
}
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.preference

import com.android.settingslib.metadata.PreferenceGroup
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.SwitchPreference

/** Factory to map [PreferenceMetadata] to [PreferenceBinding]. */
interface PreferenceBindingFactory {

    /** Returns the [PreferenceBinding] associated with the [PreferenceMetadata]. */
    fun getPreferenceBinding(metadata: PreferenceMetadata): PreferenceBinding?
}

/** Default [PreferenceBindingFactory]. */
object DefaultPreferenceBindingFactory : PreferenceBindingFactory {

    override fun getPreferenceBinding(metadata: PreferenceMetadata) =
        metadata as? PreferenceBinding
            ?: when (metadata) {
                is SwitchPreference -> SwitchPreferenceBinding.INSTANCE
                is PreferenceGroup -> PreferenceGroupBinding.INSTANCE
                is PreferenceScreenCreator -> PreferenceScreenBinding.INSTANCE
                else -> DefaultPreferenceBinding
            }
}

/** A preference key based binding factory. */
class KeyedPreferenceBindingFactory(private val bindings: Map<String, PreferenceBinding>) :
    PreferenceBindingFactory {

    override fun getPreferenceBinding(metadata: PreferenceMetadata) =
        bindings[metadata.key] ?: DefaultPreferenceBindingFactory.getPreferenceBinding(metadata)
}
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.preference

import android.content.Context
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.PreferenceTitleProvider

/** Binding of preference group associated with [PreferenceCategory]. */
interface PreferenceScreenBinding : PreferenceBinding {

    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
        super.bind(preference, metadata)
        val context = preference.context
        val screenMetadata = metadata as PreferenceScreenMetadata
        // Pass the preference key to fragment, so that the fragment could find associated
        // preference screen registered in PreferenceScreenRegistry
        preference.extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
        if (preference is PreferenceScreen) {
            val screenTitle = screenMetadata.screenTitle
            preference.title =
                if (screenTitle != 0) {
                    context.getString(screenTitle)
                } else {
                    screenMetadata.getScreenTitle(context)
                        ?: (this as? PreferenceTitleProvider)?.getTitle(context)
                }
        }
    }

    companion object {
        @JvmStatic val INSTANCE = object : PreferenceScreenBinding {}
    }
}

/** Binding of preference group associated with [PreferenceCategory]. */
interface PreferenceGroupBinding : PreferenceBinding {

    override fun createWidget(context: Context) = PreferenceCategory(context)

    companion object {
        @JvmStatic val INSTANCE = object : PreferenceGroupBinding {}
    }
}

/** A boolean value type preference associated with [SwitchPreferenceCompat]. */
interface SwitchPreferenceBinding : PreferenceBinding {

    override fun createWidget(context: Context): Preference = SwitchPreferenceCompat(context)

    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
        super.bind(preference, metadata)
        (metadata as? PersistentPreference<*>)
            ?.storage(preference.context)
            ?.getValue(metadata.key, Boolean::class.javaObjectType)
            ?.let { (preference as SwitchPreferenceCompat).isChecked = it }
    }

    companion object {
        @JvmStatic val INSTANCE = object : SwitchPreferenceBinding {}
    }
}

/** Default [PreferenceBinding] for [Preference]. */
object DefaultPreferenceBinding : PreferenceBinding
Loading