• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.regionalpreferences;
18 
19 import static android.app.settings.SettingsEnums.ACTION_CHANGE_PREFERRED_LANGUAGE_REGION_POSITIVE_BTN_CLICKED;
20 import static android.app.settings.SettingsEnums.ACTION_CHANGE_PREFERRED_LANGUAGE_REGION_NEGATIVE_BTN_CLICKED;
21 import static android.app.settings.SettingsEnums.ACTION_CHANGE_REGION_DIALOG_NEGATIVE_BTN_CLICKED;
22 import static android.app.settings.SettingsEnums.ACTION_CHANGE_REGION_DIALOG_POSITIVE_BTN_CLICKED;
23 import static android.app.settings.SettingsEnums.CHANGE_REGION_DIALOG;
24 
25 import android.app.Dialog;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.os.Bundle;
29 import android.os.LocaleList;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.widget.TextView;
34 
35 import androidx.annotation.NonNull;
36 import androidx.annotation.Nullable;
37 import androidx.annotation.VisibleForTesting;
38 import androidx.appcompat.app.AlertDialog;
39 
40 import com.android.internal.app.LocalePicker;
41 import com.android.internal.app.LocaleStore;
42 import com.android.settings.R;
43 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
44 import com.android.settings.overlay.FeatureFactory;
45 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
46 
47 import java.util.Locale;
48 import java.util.Set;
49 
50 /**
51  * Create a dialog for system region events.
52  */
53 public class RegionDialogFragment extends InstrumentedDialogFragment {
54 
55     public static final String ARG_CALLING_PAGE = "arg_calling_page";
56     public static final int CALLING_PAGE_LANGUAGE_CHOOSE_A_REGION = 0;
57     public static final int CALLING_PAGE_REGIONAL_PREFERENCES_REGION_PICKER = 1;
58     public static final int DIALOG_CHANGE_SYSTEM_LOCALE_REGION = 1;
59     public static final int DIALOG_CHANGE_PREFERRED_LOCALE_REGION = 2;
60     public static final String ARG_DIALOG_TYPE = "arg_dialog_type";
61     public static final String ARG_TARGET_LOCALE = "arg_target_locale";
62     public static final String ARG_REPLACED_TARGET_LOCALE = "arg_replaced_target_locale";
63 
64     private static final String TAG = "RegionDialogFragment";
65 
66     /**
67      * Use this factory method to create a new instance of
68      * this fragment using the provided parameters.
69      *
70      * @return A new instance of fragment RegionDialogFragment.
71      */
72     @NonNull
newInstance()73     public static RegionDialogFragment newInstance() {
74         return new RegionDialogFragment();
75     }
76 
77     @Override
getMetricsCategory()78     public int getMetricsCategory() {
79         return CHANGE_REGION_DIALOG;
80     }
81 
82     @NonNull
83     @Override
onCreateDialog(@ullable Bundle savedInstanceState)84     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
85         // TODO(385834414): Migrate to use MaterialAlertDialogBuilder
86         RegionDialogController controller = getRegionDialogController(getContext(), this);
87         RegionDialogController.DialogContent dialogContent = controller.getDialogContent();
88         ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(getContext()).inflate(
89                 R.layout.locale_dialog, null);
90         setDialogTitle(viewGroup, dialogContent.mTitle);
91         setDialogMessage(viewGroup, dialogContent.mMessage);
92         AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(viewGroup);
93         if (!dialogContent.mPositiveButton.isEmpty()) {
94             builder.setPositiveButton(dialogContent.mPositiveButton, controller);
95         }
96         if (!dialogContent.mNegativeButton.isEmpty()) {
97             builder.setNegativeButton(dialogContent.mNegativeButton, controller);
98         }
99         return builder.create();
100     }
101 
setDialogTitle(View root, String content)102     private static void setDialogTitle(View root, String content) {
103         TextView titleView = root.findViewById(R.id.dialog_title);
104         if (titleView == null) {
105             return;
106         }
107         titleView.setText(content);
108     }
109 
setDialogMessage(View root, String content)110     private static void setDialogMessage(View root, String content) {
111         TextView textView = root.findViewById(R.id.dialog_msg);
112         if (textView == null) {
113             return;
114         }
115         textView.setText(content);
116     }
117 
118     @VisibleForTesting
getRegionDialogController(Context context, RegionDialogFragment dialogFragment)119     RegionDialogController getRegionDialogController(Context context,
120             RegionDialogFragment dialogFragment) {
121         return new RegionDialogController(context, dialogFragment);
122     }
123 
124     class RegionDialogController implements DialogInterface.OnClickListener {
125         private final Context mContext;
126         private final int mDialogType;
127         private final int mCallingPage;
128         private final LocaleStore.LocaleInfo mLocaleInfo;
129         private final Locale mReplacedLocale;
130         private final MetricsFeatureProvider mMetricsFeatureProvider;
131 
RegionDialogController( @onNull Context context, @NonNull RegionDialogFragment dialogFragment)132         RegionDialogController(
133                 @NonNull Context context, @NonNull RegionDialogFragment dialogFragment) {
134             mContext = context;
135             Bundle arguments = dialogFragment.getArguments();
136             mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
137             mCallingPage = arguments.getInt(ARG_CALLING_PAGE);
138             mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
139             mReplacedLocale = (Locale) arguments.getSerializable(ARG_REPLACED_TARGET_LOCALE);
140             mMetricsFeatureProvider =
141                 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
142         }
143 
144         @Override
onClick(@onNull DialogInterface dialog, int which)145         public void onClick(@NonNull DialogInterface dialog, int which) {
146             if (mDialogType == DIALOG_CHANGE_SYSTEM_LOCALE_REGION
147                     || mDialogType == DIALOG_CHANGE_PREFERRED_LOCALE_REGION) {
148                 if (which == DialogInterface.BUTTON_POSITIVE) {
149                     updateRegion(mLocaleInfo.getLocale().toLanguageTag());
150                     mMetricsFeatureProvider.action(
151                             mContext,
152                             mDialogType == DIALOG_CHANGE_SYSTEM_LOCALE_REGION
153                                 ? ACTION_CHANGE_REGION_DIALOG_POSITIVE_BTN_CLICKED
154                                 : ACTION_CHANGE_PREFERRED_LANGUAGE_REGION_POSITIVE_BTN_CLICKED,
155                             mCallingPage);
156                     dismiss();
157                     if (getActivity() != null) {
158                         getActivity().finish();
159                     }
160                 } else {
161                     mMetricsFeatureProvider.action(
162                             mContext,
163                             mDialogType == DIALOG_CHANGE_SYSTEM_LOCALE_REGION
164                                 ? ACTION_CHANGE_REGION_DIALOG_NEGATIVE_BTN_CLICKED
165                                 : ACTION_CHANGE_PREFERRED_LANGUAGE_REGION_NEGATIVE_BTN_CLICKED,
166                             mCallingPage);
167                     dismiss();
168                 }
169             }
170         }
171 
172         @VisibleForTesting
getDialogContent()173         DialogContent getDialogContent() {
174             DialogContent dialogContent = new DialogContent();
175             switch (mDialogType) {
176                 case DIALOG_CHANGE_SYSTEM_LOCALE_REGION:
177                     dialogContent.mTitle = String.format(mContext.getString(
178                         R.string.title_change_system_region),
179                         mLocaleInfo.getLocale().getDisplayCountry());
180                     dialogContent.mMessage = mContext.getString(
181                         R.string.desc_notice_device_region_change,
182                         Locale.getDefault().getDisplayLanguage());
183                     dialogContent.mPositiveButton = mContext.getString(
184                         R.string.button_label_confirmation_of_system_locale_change);
185                     dialogContent.mNegativeButton = mContext.getString(R.string.cancel);
186                     break;
187                 case DIALOG_CHANGE_PREFERRED_LOCALE_REGION:
188                     dialogContent.mTitle = String.format(mContext.getString(
189                             R.string.title_change_system_region),
190                         mLocaleInfo.getLocale().getDisplayCountry());
191                     dialogContent.mMessage = mContext.getString(
192                         R.string.desc_notice_device_region_change_for_preferred_language,
193                         mLocaleInfo.getFullNameNative(),
194                         LocaleStore.getLocaleInfo(mReplacedLocale).getFullNameNative());
195                     dialogContent.mPositiveButton = mContext.getString(
196                         R.string.button_label_confirmation_of_system_locale_change);
197                     dialogContent.mNegativeButton = mContext.getString(R.string.cancel);
198                     break;
199                 default:
200                     break;
201             }
202             return dialogContent;
203         }
204 
updateRegion(String selectedLanguageTag)205         private void updateRegion(String selectedLanguageTag) {
206             Locale[] newLocales = getUpdatedLocales(Locale.forLanguageTag(selectedLanguageTag));
207             LocalePicker.updateLocales(new LocaleList(newLocales));
208         }
209 
getUpdatedLocales(Locale selectedLocale)210         private Locale[] getUpdatedLocales(Locale selectedLocale) {
211             LocaleList localeList = LocaleList.getDefault();
212             Locale[] newLocales = new Locale[localeList.size()];
213             for (int i = 0; i < localeList.size(); i++) {
214                 Locale target = localeList.get(i);
215                 if (sameLanguageAndScript(selectedLocale, target)) {
216                     newLocales[i] = appendLocaleExtension(selectedLocale);
217                 } else {
218                     newLocales[i] = localeList.get(i);
219                 }
220             }
221             return newLocales;
222         }
223 
appendLocaleExtension(Locale selectedLocale)224         private Locale appendLocaleExtension(Locale selectedLocale) {
225             Locale systemLocale = Locale.getDefault();
226             Set<Character> extensionKeys = systemLocale.getExtensionKeys();
227             Locale.Builder builder = new Locale.Builder();
228             builder.setLocale(selectedLocale);
229             if (!extensionKeys.isEmpty()) {
230                 for (Character extKey : extensionKeys) {
231                     builder.setExtension(extKey, systemLocale.getExtension(extKey));
232                 }
233             }
234             return builder.build();
235         }
236 
sameLanguageAndScript(Locale source, Locale target)237         private static boolean sameLanguageAndScript(Locale source, Locale target) {
238             String sourceLanguage = source.getLanguage();
239             String targetLanguage = target.getLanguage();
240             String sourceLocaleScript = source.getScript();
241             String targetLocaleScript = target.getScript();
242             if (sourceLanguage.equals(targetLanguage)) {
243                 if (!sourceLocaleScript.isEmpty() && !targetLocaleScript.isEmpty()) {
244                     return sourceLocaleScript.equals(targetLocaleScript);
245                 }
246                 return true;
247             }
248             return false;
249         }
250 
251         @VisibleForTesting
252         static class DialogContent {
253             String mTitle = "";
254             String mMessage = "";
255             String mPositiveButton = "";
256             String mNegativeButton = "";
257         }
258     }
259 }
260