From 0ef3c2ca0d81c26f757b5a7bff323eb850ccd07c Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Wed, 25 Dec 2024 10:32:58 +0800 Subject: [PATCH 1/8] [Catalyst] Implement get{Read,Write}Permissions for Brightness level NO_IFTTT=Catalyst migration Test: Devtool Fix: 385035021 Flag: com.android.settings.flags.catalyst_display_settings_screen Change-Id: Idf07b75ab9776b33bcddb1ffc93eca7594524c14 --- .../android/settings/display/BrightnessLevelPreference.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/settings/display/BrightnessLevelPreference.kt b/src/com/android/settings/display/BrightnessLevelPreference.kt index 5ee286a96eb..9dc68db7834 100644 --- a/src/com/android/settings/display/BrightnessLevelPreference.kt +++ b/src/com/android/settings/display/BrightnessLevelPreference.kt @@ -15,6 +15,7 @@ */ package com.android.settings.display +import android.Manifest import android.app.ActivityOptions import android.content.Context import android.content.Intent @@ -36,6 +37,7 @@ import com.android.settingslib.datastore.DataChangeReason import com.android.settingslib.datastore.HandlerExecutor import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.KeyedObserver +import com.android.settingslib.datastore.Permissions import com.android.settingslib.datastore.SettingsSystemStore import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN @@ -95,6 +97,11 @@ class BrightnessLevelPreference : preference.isPersistent = false } + override fun getReadPermissions(context: Context) = + Permissions.allOf(Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS) + + override fun getWritePermissions(context: Context) = Permissions.EMPTY + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW From e7fbf8f92c9c694ce28fe80317e9d3f9557d110e Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 25 Dec 2024 09:00:41 +0800 Subject: [PATCH 2/8] [Catalyst] Refactor DarkModeScreen By virtue of KeyValueStore abstraction, the per-fragment state could be moved into storage. NO_IFTTT=Catalyst only Bug: 375132235 Flag: com.android.settings.flags.catalyst_dark_ui_mode Test: Manual Change-Id: Ifff1c6e0b51cda981337b84008713c35a0a724ea --- .../display/darkmode/DarkModeScreen.kt | 95 ++----------------- .../display/darkmode/DarkModeStorage.kt | 73 ++++++++++++++ 2 files changed, 82 insertions(+), 86 deletions(-) create mode 100644 src/com/android/settings/display/darkmode/DarkModeStorage.kt diff --git a/src/com/android/settings/display/darkmode/DarkModeScreen.kt b/src/com/android/settings/display/darkmode/DarkModeScreen.kt index 87f9f23bf70..a768875ff00 100644 --- a/src/com/android/settings/display/darkmode/DarkModeScreen.kt +++ b/src/com/android/settings/display/darkmode/DarkModeScreen.kt @@ -17,24 +17,16 @@ package com.android.settings.display.darkmode import android.Manifest -import android.app.UiModeManager -import android.content.BroadcastReceiver import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.content.res.Configuration import android.os.PowerManager import androidx.preference.Preference import com.android.settings.R import com.android.settings.flags.Flags import com.android.settingslib.PrimarySwitchPreference import com.android.settingslib.datastore.KeyValueStore -import com.android.settingslib.datastore.NoOpKeyedObservable import com.android.settingslib.datastore.Permissions import com.android.settingslib.metadata.BooleanValue import com.android.settingslib.metadata.PersistentPreference -import com.android.settingslib.metadata.PreferenceLifecycleContext -import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.ProvidePreferenceScreen @@ -43,26 +35,17 @@ import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceScreenBinding import com.android.settingslib.preference.PreferenceScreenCreator -import java.util.WeakHashMap // LINT.IfChange @ProvidePreferenceScreen -class DarkModeScreen : +class DarkModeScreen(context: Context) : PreferenceScreenCreator, PreferenceScreenBinding, PersistentPreference, BooleanValue, - PreferenceSummaryProvider, - PreferenceLifecycleProvider { + PreferenceSummaryProvider { - /** - * States for different screens. - * - * The "Dark mode" appears in several screens. And in Android split-screen mode, more than one - * "Dark mode" settings could be displayed at the same time. As [PreferenceScreenCreator] works - * like singleton, we need to register different broadcast receivers for different screens. - */ - private val fragmentStates = WeakHashMap() + private val darkModeStorage = DarkModeStorage(context) override val key: String get() = KEY @@ -99,23 +82,23 @@ class DarkModeScreen : override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} - override fun storage(context: Context): KeyValueStore = DarkModeStorage(context) + override fun storage(context: Context): KeyValueStore = darkModeStorage override fun createWidget(context: Context) = PrimarySwitchPreference(context) override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) if (preference is DarkModePreference) preference.setCatalystEnabled(true) - val context = preference.context - val primarySwitchPreference = preference as PrimarySwitchPreference - primarySwitchPreference.isSwitchEnabled = !context.isPowerSaveMode() - primarySwitchPreference.isChecked = context.isDarkMode() + (preference as PrimarySwitchPreference).apply { + isSwitchEnabled = isEnabled() + isChecked = darkModeStorage.getBoolean(KEY) == true + } } override fun isEnabled(context: Context) = !context.isPowerSaveMode() override fun getSummary(context: Context): CharSequence? { - val active = context.isDarkMode() + val active = darkModeStorage.getBoolean(KEY) == true return when { !context.isPowerSaveMode() -> AutoDarkTheme.getStatus(context, active) active -> context.getString(R.string.dark_ui_mode_disabled_summary_dark_theme_on) @@ -123,71 +106,11 @@ class DarkModeScreen : } } - override fun onStart(context: PreferenceLifecycleContext) { - val broadcastReceiver = - object : BroadcastReceiver() { - override fun onReceive(receiverContext: Context, intent: Intent) { - context.notifyPreferenceChange(KEY) - } - } - context.registerReceiver( - broadcastReceiver, - IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED), - ) - - val darkModeObserver = DarkModeObserver(context) - darkModeObserver.subscribe { context.notifyPreferenceChange(KEY) } - - fragmentStates[context] = FragmentState(broadcastReceiver, darkModeObserver) - } - - override fun onStop(context: PreferenceLifecycleContext) { - fragmentStates.remove(context)?.run { - context.unregisterReceiver(broadcastReceiver) - darkModeObserver.unsubscribe() - } - } - - private class FragmentState( - val broadcastReceiver: BroadcastReceiver, - val darkModeObserver: DarkModeObserver, - ) - - /** - * Abstract storage for dark mode settings. - * - * The underlying storage is manipulated by [UiModeManager] but we do not need to worry about - * the details. Additionally, the observer is for UI purpose only right now, so use - * [NoOpKeyedObservable]. - */ - @Suppress("UNCHECKED_CAST") - private class DarkModeStorage(private val context: Context) : - NoOpKeyedObservable(), KeyValueStore { - - override fun contains(key: String) = key == KEY - - override fun getValue(key: String, valueType: Class) = - when { - key == KEY && valueType == Boolean::class.javaObjectType -> - context.isDarkMode() as T - else -> null - } - - override fun setValue(key: String, valueType: Class, value: T?) { - if (key == KEY && value is Boolean) { - context.getSystemService(UiModeManager::class.java)?.setNightModeActivated(value) - } - } - } - companion object { const val KEY = "dark_ui_mode" private fun Context.isPowerSaveMode() = getSystemService(PowerManager::class.java)?.isPowerSaveMode == true - - private fun Context.isDarkMode() = - (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES) != 0 } } // LINT.ThenChange(../DarkUIPreferenceController.java) diff --git a/src/com/android/settings/display/darkmode/DarkModeStorage.kt b/src/com/android/settings/display/darkmode/DarkModeStorage.kt new file mode 100644 index 00000000000..9c53379c2d0 --- /dev/null +++ b/src/com/android/settings/display/darkmode/DarkModeStorage.kt @@ -0,0 +1,73 @@ +/* + * 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.display.darkmode + +import android.app.UiModeManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.Configuration +import android.os.PowerManager +import com.android.settingslib.datastore.AbstractKeyedDataObservable +import com.android.settingslib.datastore.DataChangeReason +import com.android.settingslib.datastore.KeyValueStore + +/** + * Abstract storage for dark mode settings. + * + * The underlying storage is manipulated by [UiModeManager] but we do not need to worry about the + * details. + */ +@Suppress("UNCHECKED_CAST") +internal class DarkModeStorage(private val context: Context) : + AbstractKeyedDataObservable(), KeyValueStore { + private lateinit var broadcastReceiver: BroadcastReceiver + private lateinit var darkModeObserver: DarkModeObserver + + override fun contains(key: String) = true + + override fun getValue(key: String, valueType: Class) = context.isDarkMode() as T + + private fun Context.isDarkMode() = + (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES) != 0 + + override fun setValue(key: String, valueType: Class, value: T?) { + context.getSystemService(UiModeManager::class.java)?.setNightModeActivated(value as Boolean) + } + + override fun onFirstObserverAdded() { + broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + notifyChange(DataChangeReason.UPDATE) + } + } + context.registerReceiver( + broadcastReceiver, + IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED), + ) + + darkModeObserver = DarkModeObserver(context) + darkModeObserver.subscribe { notifyChange(DataChangeReason.UPDATE) } + } + + override fun onLastObserverRemoved() { + context.unregisterReceiver(broadcastReceiver) + darkModeObserver.unsubscribe() + } +} From 833a0e3c926294ab5fe42acb1636e93e1823bccd Mon Sep 17 00:00:00 2001 From: danielwbhuang Date: Fri, 20 Dec 2024 20:43:48 +0800 Subject: [PATCH 3/8] Show dialog when user chnages the region 1. show dialog 2. change the region of the top locale Bug: 385047778 Flag: com.android.settings.flags.regional_preferences_api_enabled Test: check hsv, atest Change-Id: I9746cdec670899b3768dcd1e0aa59e1959dd7e06 --- res/values/strings.xml | 6 + .../LocalePickerWithRegionActivity.java | 39 ++++ .../RegionDialogFragment.java | 185 ++++++++++++++++++ ...ionPickerBaseListPreferenceController.java | 42 ++-- .../RegionPickerFragment.java | 2 + .../LocalePickerWithRegionActivityTest.java | 8 + 6 files changed, 258 insertions(+), 24 deletions(-) create mode 100644 src/com/android/settings/regionalpreferences/RegionDialogFragment.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 7386eabaa38..c4c7fec5497 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -479,6 +479,12 @@ https://support.google.com/android?p=per_language_app_settings + + Change region to %s ? + + + Your device will keep %s as a system language + Change system language to %s ? diff --git a/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java index 3cf76837ea5..ebd3f887b5e 100644 --- a/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java +++ b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java @@ -33,6 +33,10 @@ import com.android.internal.app.LocalePickerWithRegion; import com.android.internal.app.LocaleStore; import com.android.settings.R; import com.android.settings.core.SettingsBaseActivity; +import com.android.settings.flags.Flags; +import com.android.settings.regionalpreferences.RegionDialogFragment; + +import java.util.Locale; /** * An activity to show the locale picker page. @@ -45,6 +49,10 @@ public class LocalePickerWithRegionActivity extends SettingsBaseActivity private static final String TAG = LocalePickerWithRegionActivity.class.getSimpleName(); private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; private static final String CHILD_FRAGMENT_NAME = "LocalePickerWithRegion"; + private static final int DIALOG_CHANGE_LOCALE_REGION = 1; + private static final String ARG_DIALOG_TYPE = "arg_dialog_type"; + private static final String ARG_TARGET_LOCALE = "arg_target_locale"; + private static final String TAG_DIALOG_CHANGE_REGION = "dialog_change_region"; private LocalePickerWithRegion mSelector; @@ -102,6 +110,37 @@ public class LocalePickerWithRegionActivity extends SettingsBaseActivity @Override public void onLocaleSelected(LocaleStore.LocaleInfo locale) { + if (Flags.regionalPreferencesApiEnabled()) { + if (sameLanguageAndScript(locale.getLocale(), Locale.getDefault())) { + Bundle args = new Bundle(); + args.putInt(ARG_DIALOG_TYPE, DIALOG_CHANGE_LOCALE_REGION); + args.putSerializable(ARG_TARGET_LOCALE, locale); + RegionDialogFragment regionDialogFragment = RegionDialogFragment.newInstance(); + regionDialogFragment.setArguments(args); + regionDialogFragment.show(getSupportFragmentManager(), TAG_DIALOG_CHANGE_REGION); + } else { + dispose(locale); + } + } else { + dispose(locale); + } + } + + private static boolean sameLanguageAndScript(Locale source, Locale target) { + String sourceLanguage = source.getLanguage(); + String targetLanguage = target.getLanguage(); + String sourceLocaleScript = source.getScript(); + String targetLocaleScript = target.getScript(); + if (sourceLanguage.equals(targetLanguage)) { + if (!sourceLocaleScript.isEmpty() && !targetLocaleScript.isEmpty()) { + return sourceLocaleScript.equals(targetLocaleScript); + } + return true; + } + return false; + } + + private void dispose(LocaleStore.LocaleInfo locale) { final Intent intent = new Intent(); intent.putExtra(LocaleListEditor.INTENT_LOCALE_KEY, locale); setResult(RESULT_OK, intent); diff --git a/src/com/android/settings/regionalpreferences/RegionDialogFragment.java b/src/com/android/settings/regionalpreferences/RegionDialogFragment.java new file mode 100644 index 00000000000..3f6aa54ce66 --- /dev/null +++ b/src/com/android/settings/regionalpreferences/RegionDialogFragment.java @@ -0,0 +1,185 @@ +/* + * 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.regionalpreferences; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.LocaleList; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; + +import com.android.internal.app.LocalePicker; +import com.android.internal.app.LocaleStore; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +import java.util.Locale; +import java.util.Set; + +/** + * Create a dialog for system region events. + */ +public class RegionDialogFragment extends InstrumentedDialogFragment { + private static final String TAG = "RegionDialogFragment"; + static final int DIALOG_CHANGE_LOCALE_REGION = 1; + static final String ARG_DIALOG_TYPE = "arg_dialog_type"; + static final String ARG_TARGET_LOCALE = "arg_target_locale"; + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @return A new instance of fragment RegionDialogFragment. + */ + @NonNull + public static RegionDialogFragment newInstance() { + return new RegionDialogFragment(); + } + + @Override + public int getMetricsCategory() { + return 0; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + // TODO(385834414): Migrate to use MaterialAlertDialogBuilder + RegionDialogController controller = getRegionDialogController(getContext(), this); + RegionDialogController.DialogContent dialogContent = controller.getDialogContent(); + ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(getContext()).inflate( + R.layout.locale_dialog, null); + setDialogTitle(viewGroup, dialogContent.mTitle); + setDialogMessage(viewGroup, dialogContent.mMessage); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(viewGroup); + if (!dialogContent.mPositiveButton.isEmpty()) { + builder.setPositiveButton(dialogContent.mPositiveButton, controller); + } + if (!dialogContent.mNegativeButton.isEmpty()) { + builder.setNegativeButton(dialogContent.mNegativeButton, controller); + } + return builder.create(); + } + + private static void setDialogTitle(View root, String content) { + TextView titleView = root.findViewById(R.id.dialog_title); + if (titleView == null) { + return; + } + titleView.setText(content); + } + + private static void setDialogMessage(View root, String content) { + TextView textView = root.findViewById(R.id.dialog_msg); + if (textView == null) { + return; + } + textView.setText(content); + } + + @VisibleForTesting + RegionDialogController getRegionDialogController(Context context, + RegionDialogFragment dialogFragment) { + return new RegionDialogController(context, dialogFragment); + } + + class RegionDialogController implements DialogInterface.OnClickListener { + private final Context mContext; + private final int mDialogType; + private final LocaleStore.LocaleInfo mLocaleInfo; + + RegionDialogController( + @NonNull Context context, @NonNull RegionDialogFragment dialogFragment) { + mContext = context; + Bundle arguments = dialogFragment.getArguments(); + mDialogType = arguments.getInt(ARG_DIALOG_TYPE); + mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE); + } + + @Override + public void onClick(@NonNull DialogInterface dialog, int which) { + if (mDialogType == DIALOG_CHANGE_LOCALE_REGION) { + if (which == DialogInterface.BUTTON_POSITIVE) { + updateRegion(mLocaleInfo.getLocale().toLanguageTag()); + } + dismiss(); + if (getActivity() != null) { + getActivity().finish(); + } + } + } + + @VisibleForTesting + DialogContent getDialogContent() { + DialogContent dialogContent = new DialogContent(); + switch (mDialogType) { + case DIALOG_CHANGE_LOCALE_REGION: + dialogContent.mTitle = String.format(mContext.getString( + R.string.title_change_system_region), + mLocaleInfo.getLocale().getDisplayCountry()); + dialogContent.mMessage = mContext.getString( + R.string.desc_notice_device_region_change, + Locale.getDefault().getDisplayLanguage()); + dialogContent.mPositiveButton = mContext.getString( + R.string.button_label_confirmation_of_system_locale_change); + dialogContent.mNegativeButton = mContext.getString(R.string.cancel); + break; + default: + break; + } + return dialogContent; + } + + private void updateRegion(String selectedLanguageTag) { + LocaleList localeList = LocaleList.getDefault(); + Locale systemLocale = Locale.getDefault(); + Set extensionKeys = systemLocale.getExtensionKeys(); + Locale selectedLocale = Locale.forLanguageTag(selectedLanguageTag); + Locale.Builder builder = new Locale.Builder(); + builder.setLocale(selectedLocale); + if (!extensionKeys.isEmpty()) { + for (Character extKey : extensionKeys) { + builder.setExtension(extKey, systemLocale.getExtension(extKey)); + } + } + Locale newLocale = builder.build(); + Locale[] resultLocales = new Locale[localeList.size()]; + resultLocales[0] = newLocale; + for (int i = 1; i < localeList.size(); i++) { + resultLocales[i] = localeList.get(i); + } + LocalePicker.updateLocales(new LocaleList(resultLocales)); + } + + @VisibleForTesting + static class DialogContent { + String mTitle = ""; + String mMessage = ""; + String mPositiveButton = ""; + String mNegativeButton = ""; + } + } +} diff --git a/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java b/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java index 6c2c8b63331..cb3b82b57cd 100644 --- a/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java +++ b/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java @@ -17,18 +17,19 @@ package com.android.settings.regionalpreferences; import android.content.Context; -import android.os.LocaleList; +import android.os.Bundle; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.internal.annotations.Initializer; import com.android.internal.app.LocaleCollectorBase; import com.android.internal.app.LocaleHelper; -import com.android.internal.app.LocalePicker; import com.android.internal.app.LocaleStore; import com.android.internal.app.LocaleStore.LocaleInfo; import com.android.settings.core.BasePreferenceController; @@ -45,9 +46,12 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe private static final String TAG = "RegionPickerBaseListPreferenceController"; private static final String KEY_SUGGESTED = "suggested"; + private static final String TAG_DIALOG_CHANGE_REGION = "dialog_change_region"; private PreferenceCategory mPreferenceCategory; private Set mLocaleList; private ArrayList mLocaleOptions; + private Fragment mParent; + private FragmentManager mFragmentManager; public RegionPickerBaseListPreferenceController(@NonNull Context context, @NonNull String preferenceKey) { @@ -58,6 +62,10 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe mLocaleOptions.ensureCapacity(mLocaleList.size()); } + public void setFragment(@NonNull Fragment parent) { + mParent = parent; + } + @Override @Initializer public void displayPreference(@NonNull PreferenceScreen screen) { @@ -156,28 +164,14 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe if (localeInfo.getLocale().equals(Locale.getDefault())) { return; } - updateRegion(localeInfo.getLocale().toLanguageTag()); - updatePreferences(); - } - private void updateRegion(String selectedLanguageTag) { - LocaleList localeList = LocaleList.getDefault(); - Locale systemLocale = Locale.getDefault(); - Set extensionKeys = systemLocale.getExtensionKeys(); - Locale selectedLocale = Locale.forLanguageTag(selectedLanguageTag); - Locale.Builder builder = new Locale.Builder(); - builder.setLocale(selectedLocale); - if (!extensionKeys.isEmpty()) { - for (Character extKey : extensionKeys) { - builder.setExtension(extKey, systemLocale.getExtension(extKey)); - } - } - Locale newLocale = builder.build(); - Locale[] resultLocales = new Locale[localeList.size()]; - resultLocales[0] = newLocale; - for (int i = 1; i < localeList.size(); i++) { - resultLocales[i] = localeList.get(i); - } - LocalePicker.updateLocales(new LocaleList(resultLocales)); + mFragmentManager = mParent.getChildFragmentManager(); + Bundle args = new Bundle(); + args.putInt(RegionDialogFragment.ARG_DIALOG_TYPE, + RegionDialogFragment.DIALOG_CHANGE_LOCALE_REGION); + args.putSerializable(RegionDialogFragment.ARG_TARGET_LOCALE, localeInfo); + RegionDialogFragment regionDialogFragment = RegionDialogFragment.newInstance(); + regionDialogFragment.setArguments(args); + regionDialogFragment.show(mFragmentManager, TAG_DIALOG_CHANGE_REGION); } } diff --git a/src/com/android/settings/regionalpreferences/RegionPickerFragment.java b/src/com/android/settings/regionalpreferences/RegionPickerFragment.java index b675d2a2551..b1759f18b84 100644 --- a/src/com/android/settings/regionalpreferences/RegionPickerFragment.java +++ b/src/com/android/settings/regionalpreferences/RegionPickerFragment.java @@ -75,6 +75,8 @@ public class RegionPickerFragment extends DashboardFragment{ new SystemRegionAllListPreferenceController( context, KEY_PREFERENCE_SYSTEM_REGION_LIST, parentLocaleInfo); final List controllers = new ArrayList<>(); + mSuggestedListPreferenceController.setFragment(this); + mSystemRegionAllListPreferenceController.setFragment(this); controllers.add(mSuggestedListPreferenceController); controllers.add(mSystemRegionAllListPreferenceController); return controllers; diff --git a/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java index b2541472a5f..f6096d1696e 100644 --- a/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java @@ -6,10 +6,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import android.app.Activity; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import com.android.internal.app.LocaleStore; +import com.android.settings.flags.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -24,6 +28,8 @@ public class LocalePickerWithRegionActivityTest { private LocalePickerWithRegionActivity mActivity; + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -33,6 +39,7 @@ public class LocalePickerWithRegionActivityTest { } @Test + @DisableFlags(Flags.FLAG_REGIONAL_PREFERENCES_API_ENABLED) public void onLocaleSelected_resultShouldBeOK() { final ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); mActivity.onLocaleSelected(mock(LocaleStore.LocaleInfo.class)); @@ -41,6 +48,7 @@ public class LocalePickerWithRegionActivityTest { } @Test + @DisableFlags(Flags.FLAG_REGIONAL_PREFERENCES_API_ENABLED) public void onLocaleSelected_localeInfoShouldBeSentBack() { final ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); mActivity.onLocaleSelected(mock(LocaleStore.LocaleInfo.class)); From 1f5b0e9a7cd6bf579a460a33db6f08f50c4a1666 Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Wed, 25 Dec 2024 14:14:26 +0800 Subject: [PATCH 4/8] [Catalyst] Implement get{Read,Write}Permissions for Always show time and info NO_IFTTT=Catalyst migration Test: devtool Bug: 385272042 Flag: com.android.settings.flags.catalyst_lockscreen_from_display_settings Change-Id: I67513e205113df531accd9f84480d7921144df52 --- .../settings/display/AmbientDisplayAlwaysOnPreference.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt index 7a6df8e8cf4..2d2fe2b2d70 100644 --- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt +++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt @@ -71,6 +71,10 @@ class AmbientDisplayAlwaysOnPreference : override fun storage(context: Context): KeyValueStore = Storage(context) + override fun getReadPermissions(context: Context) = SettingsSecureStore.getReadPermissions() + + override fun getWritePermissions(context: Context) = SettingsSecureStore.getWritePermissions() + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW From 249a4426b6d995a6cf06b88d51f1572a7c3fb5a2 Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Wed, 25 Dec 2024 14:48:57 +0800 Subject: [PATCH 5/8] [Catalyst] Implement get{Read,Write}Permissions for Adaptive brightness NO_IFTTT=Catalyst migration Test: Devtool Bug: 385266613 Flag: com.android.settings.flags.catalyst_display_settings_screen Change-Id: I32e8b5e30269397b6198bcd87d366ada73d84893 --- src/com/android/settings/display/AutoBrightnessScreen.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/display/AutoBrightnessScreen.kt b/src/com/android/settings/display/AutoBrightnessScreen.kt index 319d95bb15c..945788aecde 100644 --- a/src/com/android/settings/display/AutoBrightnessScreen.kt +++ b/src/com/android/settings/display/AutoBrightnessScreen.kt @@ -65,6 +65,10 @@ class AutoBrightnessScreen : override fun storage(context: Context): KeyValueStore = AutoBrightnessDataStore(SettingsSystemStore.get(context)) + override fun getReadPermissions(context: Context) = SettingsSystemStore.getReadPermissions() + + override fun getWritePermissions(context: Context) = SettingsSystemStore.getWritePermissions() + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW From 554da2d561ece8c4ad8dc775d263927b21b41441 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 25 Dec 2024 15:23:13 +0800 Subject: [PATCH 6/8] Fix TetherPreference summary missing PreferenceController need a constructor(context, key) to work in xml. Bug: 377146536 Flag: EXEMPT bug fix Test: manual - on Network & internet Change-Id: Ib3c0155eacdff128333abf04a8103300623f4c2a --- .../android/settings/network/TetherPreferenceController.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/network/TetherPreferenceController.kt b/src/com/android/settings/network/TetherPreferenceController.kt index 524eb7836fc..3def5fc0992 100644 --- a/src/com/android/settings/network/TetherPreferenceController.kt +++ b/src/com/android/settings/network/TetherPreferenceController.kt @@ -42,7 +42,9 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class TetherPreferenceController( +class TetherPreferenceController +@JvmOverloads +constructor( context: Context, key: String, private val tetheredRepository: TetheredRepository = TetheredRepository(context), From 13cac3971ee916ed9e36a104398af361c9f3b0dd Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 25 Dec 2024 16:29:09 +0800 Subject: [PATCH 7/8] [Catalyst] Implement get{Read,Write}Permissions for Battery level NO_IFTTT=Catalyst only Fix: 385290271 Flag: com.android.settings.flags.catalyst_power_usage_summary_screen Test: devtool Change-Id: I30e2a698a9d8544684b4369519d174aba560e566 --- .../android/settings/fuelgauge/BatteryHeaderPreference.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt b/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt index 49f219f8eea..9029a8511ff 100644 --- a/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt +++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt @@ -24,6 +24,7 @@ import com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType import com.android.settingslib.Utils import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.NoOpKeyedObservable +import com.android.settingslib.datastore.Permissions import com.android.settingslib.fuelgauge.BatteryUtils import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceLifecycleContext @@ -102,9 +103,13 @@ class BatteryHeaderPreference : override fun getMaxValue(context: Context): Int = 100 + override fun getReadPermissions(context: Context) = Permissions.EMPTY + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW + override fun getWritePermissions(context: Context) = Permissions.EMPTY + override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW From 68ddca4c9a1b826af6ecaacf8c08748f2f74dec7 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Wed, 25 Dec 2024 16:33:32 +0800 Subject: [PATCH 8/8] [Catalyst] Implement get{Read,Write}Permissions for Battery percentage NO_IFTTT=Catalyst only Fix: 385288692 Flag: com.android.settings.flags.catalyst_power_usage_summary_screen Test: devtool Change-Id: I42ba759cac04ffc41787b0fca3e04f64584c57c9 --- .../settings/display/BatteryPercentageSwitchPreference.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt index fb2b74c98e3..0cdca343ea8 100644 --- a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt +++ b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt @@ -49,9 +49,13 @@ class BatteryPercentageSwitchPreference : com.android.internal.R.bool.config_battery_percentage_setting_available ) + override fun getReadPermissions(context: Context) = SettingsSystemStore.getReadPermissions() + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW + override fun getWritePermissions(context: Context) = SettingsSystemStore.getWritePermissions() + override fun getWritePermit( context: Context, value: Boolean?,