From 6a52eeabbca3a4bdf72dce3b86ba1a93facf9d62 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Tue, 12 Nov 2024 14:05:00 +0800 Subject: [PATCH 1/2] [Catalyst] Support main switch bar MainSwitchBar is a view widget in activity layout stick to the top of screen UI. There is no corresponding Preference in the preference screen. For Catalyst support, introduce an invisible Preference object to manipulate with MainSwitchBar, so that the binding mechanism is still working on top of this abstraction. Bug: 332201912 Flag: EXEMPT new class Test: manual Change-Id: If50932a443c1ed3ac04d3ea2e3273724d750297d --- .../android/settings/SettingsActivity.java | 11 ++- .../settings/widget/MainSwitchBarMetadata.kt | 36 +++++++++ .../widget/MainSwitchBarPreference.kt | 79 +++++++++++++++++++ 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/com/android/settings/widget/MainSwitchBarMetadata.kt create mode 100644 src/com/android/settings/widget/MainSwitchBarPreference.kt diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index cc6bafb2562..c81d504b223 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -311,6 +311,11 @@ public class SettingsActivity extends SettingsBaseActivity } setContentView(R.layout.settings_main_prefs); + mMainSwitch = findViewById(R.id.switch_bar); + if (mMainSwitch != null) { + mMainSwitch.setMetricsCategory(lookupMetricsCategory()); + mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1); + } getSupportFragmentManager().addOnBackStackChangedListener(this); @@ -330,12 +335,6 @@ public class SettingsActivity extends SettingsBaseActivity launchSettingFragment(initialFragmentName, intent); } - mMainSwitch = findViewById(R.id.switch_bar); - if (mMainSwitch != null) { - mMainSwitch.setMetricsCategory(lookupMetricsCategory()); - mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1); - } - // see if we should show Back/Next buttons if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { diff --git a/src/com/android/settings/widget/MainSwitchBarMetadata.kt b/src/com/android/settings/widget/MainSwitchBarMetadata.kt new file mode 100644 index 00000000000..f55cfd03789 --- /dev/null +++ b/src/com/android/settings/widget/MainSwitchBarMetadata.kt @@ -0,0 +1,36 @@ +/* + * 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.settings.widget + +import android.content.Context +import androidx.preference.Preference +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.TwoStatePreference +import com.android.settingslib.preference.PreferenceBindingPlaceholder +import com.android.settingslib.preference.TwoStatePreferenceBinding + +/** Base metadata of `MainSwitchBar`. */ +interface MainSwitchBarMetadata : + TwoStatePreference, TwoStatePreferenceBinding, PreferenceBindingPlaceholder { + + override fun createWidget(context: Context) = MainSwitchBarPreference(context, this) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as MainSwitchBarPreference).updateVisibility() + } +} diff --git a/src/com/android/settings/widget/MainSwitchBarPreference.kt b/src/com/android/settings/widget/MainSwitchBarPreference.kt new file mode 100644 index 00000000000..6ed887790c8 --- /dev/null +++ b/src/com/android/settings/widget/MainSwitchBarPreference.kt @@ -0,0 +1,79 @@ +/* + * 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.settings.widget + +import android.content.Context +import android.widget.CompoundButton +import android.widget.CompoundButton.OnCheckedChangeListener +import androidx.preference.TwoStatePreference +import com.android.settings.SettingsActivity +import com.android.settingslib.metadata.PreferenceAvailabilityProvider +import com.android.settingslib.widget.MainSwitchBar + +/** Preference abstraction of the [MainSwitchBar] in settings activity. */ +class MainSwitchBarPreference(context: Context, private val metadata: MainSwitchBarMetadata) : + TwoStatePreference(context), OnCheckedChangeListener { + + private val mainSwitchBar: MainSwitchBar = (context as SettingsActivity).switchBar + + override fun setTitle(title: CharSequence?) { + mainSwitchBar.setTitle(title) + } + + override fun setSummary(summary: CharSequence?) { + mainSwitchBar.setSummary(summary) + } + + override fun setEnabled(enabled: Boolean) { + mainSwitchBar.isEnabled = enabled + } + + // Preference.setVisible is final, we cannot override it + fun updateVisibility() { + // always make preference invisible, the UI visibility is reflected on MainSwitchBar + isVisible = false + if ((metadata as? PreferenceAvailabilityProvider)?.isAvailable(context) != false) { + mainSwitchBar.show() + } else { + mainSwitchBar.hide() + } + } + + override fun setChecked(checked: Boolean) { + // remove listener to update UI only + mainSwitchBar.removeOnSwitchChangeListener(this) + mainSwitchBar.isChecked = checked + mainSwitchBar.addOnSwitchChangeListener(this) + } + + override fun onAttached() { + super.onAttached() + mainSwitchBar.addOnSwitchChangeListener(this) + } + + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + // prevent user from toggling the switch before data store operation is done + isEnabled = false + // once data store is updated, isEnabled will be reset due to rebind + persistBoolean(isChecked) + } + + override fun onDetached() { + mainSwitchBar.removeOnSwitchChangeListener(this) + super.onDetached() + } +} From d2d5a1c2f9440ed6a9263dbf2d933d4826f0208c Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Tue, 12 Nov 2024 14:11:08 +0800 Subject: [PATCH 2/2] [Catalyst] Migrate "Use Data Saver" settings Bug: 368359883 Flag: com.android.settings.flags.catalyst Test: manual Change-Id: I2ee30cdd5edbfb13b5bf67e4c3b93b787c52a767 --- .../DataSaverMainSwitchPreference.kt | 71 +++++++++++++++++++ .../settings/datausage/DataSaverScreen.kt | 3 +- .../settings/datausage/DataSaverSummary.kt | 22 ++++-- 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt diff --git a/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt b/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt new file mode 100644 index 00000000000..b05cbc88d9f --- /dev/null +++ b/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt @@ -0,0 +1,71 @@ +/* + * 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.settings.datausage + +import android.content.Context +import com.android.settings.R +import com.android.settings.widget.MainSwitchBarMetadata +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.NoOpKeyedObservable +import com.android.settingslib.metadata.PreferenceLifecycleContext +import com.android.settingslib.metadata.PreferenceLifecycleProvider +import com.android.settingslib.metadata.ReadWritePermit + +class DataSaverMainSwitchPreference(context: Context) : + MainSwitchBarMetadata, PreferenceLifecycleProvider { + + private val dataSaverBackend = DataSaverBackend(context) + private var dataSaverBackendListener: DataSaverBackend.Listener? = null + + override val key + get() = "use_data_saver" + + override val title + get() = R.string.data_saver_switch_title + + override fun storage(context: Context): KeyValueStore = DataSaverStore(dataSaverBackend) + + override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun onStart(context: PreferenceLifecycleContext) { + val listener = DataSaverBackend.Listener { context.notifyPreferenceChange(this) } + dataSaverBackendListener = listener + dataSaverBackend.addListener(listener) + } + + override fun onStop(context: PreferenceLifecycleContext) { + dataSaverBackendListener?.let { + dataSaverBackend.remListener(it) + dataSaverBackendListener = null + } + } + + @Suppress("UNCHECKED_CAST") + private class DataSaverStore(private val dataSaverBackend: DataSaverBackend) : + NoOpKeyedObservable(), KeyValueStore { + + override fun contains(key: String) = true // just assume the datastore contains the value + + override fun getValue(key: String, valueType: Class): T? = + dataSaverBackend.isDataSaverEnabled as T? + + override fun setValue(key: String, valueType: Class, value: T?) { + dataSaverBackend.isDataSaverEnabled = value as Boolean + } + } +} diff --git a/src/com/android/settings/datausage/DataSaverScreen.kt b/src/com/android/settings/datausage/DataSaverScreen.kt index eafaa1e8c03..9a9b79018a1 100644 --- a/src/com/android/settings/datausage/DataSaverScreen.kt +++ b/src/com/android/settings/datausage/DataSaverScreen.kt @@ -38,7 +38,8 @@ class DataSaverScreen : PreferenceScreenCreator { override fun fragmentClass() = DataSaverSummary::class.java - override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + override fun getPreferenceHierarchy(context: Context) = + preferenceHierarchy(this) { +DataSaverMainSwitchPreference(context) } override fun hasCompleteHierarchy() = false diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt index 8db633331bc..60de8a72539 100644 --- a/src/com/android/settings/datausage/DataSaverSummary.kt +++ b/src/com/android/settings/datausage/DataSaverSummary.kt @@ -43,26 +43,34 @@ class DataSaverSummary : DashboardFragment() { return } - dataSaverBackend = DataSaverBackend(requireContext()) + if (!isCatalystEnabled) { + dataSaverBackend = DataSaverBackend(requireContext()) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - switchBar = (activity as SettingsActivity).switchBar.apply { - setTitle(getString(R.string.data_saver_switch_title)) - show() - addOnSwitchChangeListener { _, isChecked -> onSwitchChanged(isChecked) } + if (!isCatalystEnabled) { + switchBar = (activity as SettingsActivity).switchBar.apply { + setTitle(getString(R.string.data_saver_switch_title)) + show() + addOnSwitchChangeListener { _, isChecked -> onSwitchChanged(isChecked) } + } } } override fun onResume() { super.onResume() - dataSaverBackend.addListener(dataSaverBackendListener) + if (!isCatalystEnabled) { + dataSaverBackend.addListener(dataSaverBackendListener) + } } override fun onPause() { super.onPause() - dataSaverBackend.remListener(dataSaverBackendListener) + if (!isCatalystEnabled) { + dataSaverBackend.remListener(dataSaverBackendListener) + } } private fun onSwitchChanged(isChecked: Boolean) {