From 833a0e3c926294ab5fe42acb1636e93e1823bccd Mon Sep 17 00:00:00 2001 From: danielwbhuang Date: Fri, 20 Dec 2024 20:43:48 +0800 Subject: [PATCH] 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));