• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.localepicker;
18 
19 import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
20 
21 import android.app.Activity;
22 import android.app.Dialog;
23 import android.app.settings.SettingsEnums;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.os.Bundle;
28 import android.util.Log;
29 import android.window.OnBackInvokedCallback;
30 import android.window.OnBackInvokedDispatcher;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.internal.app.LocaleStore;
36 import com.android.settings.R;
37 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
38 import com.android.settings.overlay.FeatureFactory;
39 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
40 import com.android.settingslib.utils.CustomDialogHelper;
41 
42 /**
43  * Create a dialog for system locale events.
44  */
45 public class LocaleDialogFragment extends InstrumentedDialogFragment {
46     private static final String TAG = LocaleDialogFragment.class.getSimpleName();
47 
48     static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 1;
49     static final int DIALOG_NOT_AVAILABLE_LOCALE = 2;
50     static final int DIALOG_ADD_SYSTEM_LOCALE = 3;
51 
52     static final String ARG_DIALOG_TYPE = "arg_dialog_type";
53     static final String ARG_TARGET_LOCALE = "arg_target_locale";
54     static final String ARG_SHOW_DIALOG = "arg_show_dialog";
55     static final String ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED = "arg_show_dialog_for_not_translated";
56 
57     private boolean mShouldKeepDialog;
58     private OnBackInvokedDispatcher mBackDispatcher;
59 
60     private OnBackInvokedCallback mBackCallback = () -> {
61         Log.d(TAG, "Do not back to previous page if the dialog is displaying.");
62     };
63 
newInstance()64     public static LocaleDialogFragment newInstance() {
65         return new LocaleDialogFragment();
66     }
67 
68     @Override
getMetricsCategory()69     public int getMetricsCategory() {
70         int dialogType = getArguments().getInt(ARG_DIALOG_TYPE);
71         switch (dialogType) {
72             case DIALOG_CONFIRM_SYSTEM_DEFAULT:
73                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
74             case DIALOG_NOT_AVAILABLE_LOCALE:
75                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE;
76             default:
77                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
78         }
79     }
80 
81     @Override
onSaveInstanceState(Bundle outState)82     public void onSaveInstanceState(Bundle outState) {
83         super.onSaveInstanceState(outState);
84         outState.putBoolean(ARG_SHOW_DIALOG, mShouldKeepDialog);
85     }
86 
87     @Override
onCreateDialog(Bundle savedInstanceState)88     public Dialog onCreateDialog(Bundle savedInstanceState) {
89         if (savedInstanceState != null) {
90             Bundle arguments = getArguments();
91             int type = arguments.getInt(ARG_DIALOG_TYPE);
92             mShouldKeepDialog = savedInstanceState.getBoolean(ARG_SHOW_DIALOG, false);
93             // Keep the dialog if user rotates the device, otherwise close the confirm system
94             // default dialog only when user changes the locale.
95             if ((type == DIALOG_CONFIRM_SYSTEM_DEFAULT || type == DIALOG_ADD_SYSTEM_LOCALE)
96                     && !mShouldKeepDialog) {
97                 dismiss();
98             }
99         }
100 
101         mShouldKeepDialog = true;
102         LocaleListEditor parentFragment = (LocaleListEditor) getParentFragment();
103         LocaleDialogController controller = getLocaleDialogController(getContext(), this,
104                 parentFragment);
105         Dialog dialog = createDialog(getContext(), controller);
106         dialog.setCanceledOnTouchOutside(false);
107         getOnBackInvokedDispatcher(dialog).registerOnBackInvokedCallback(PRIORITY_DEFAULT,
108                 mBackCallback);
109         dialog.setOnDismissListener(dialogInterface -> {
110             getOnBackInvokedDispatcher(dialog).unregisterOnBackInvokedCallback(
111                     mBackCallback);
112         });
113 
114         return dialog;
115     }
116 
createDialog(Context context, LocaleDialogController controller)117     private Dialog createDialog(Context context, LocaleDialogController controller) {
118         CustomDialogHelper dialogHelper = new CustomDialogHelper(context);
119         LocaleDialogController.DialogContent dialogContent = controller.getDialogContent();
120         dialogHelper.setIcon(context.getDrawable(R.drawable.ic_settings_language_32dp))
121                 .setTitle(dialogContent.mTitle)
122                 .setMessage(dialogContent.mMessage)
123                 .setIconPadding(0,
124                         context.getResources().getDimensionPixelSize(
125                                 R.dimen.locale_picker_dialog_icon_padding),
126                         0, 0)
127                 .setTitlePadding(0,
128                         context.getResources().getDimensionPixelSize(
129                                 R.dimen.locale_picker_dialog_title_padding),
130                         0,
131                         context.getResources().getDimensionPixelSize(
132                                 R.dimen.locale_picker_dialog_title_padding))
133                 .setMessagePadding(context.getResources().getDimensionPixelSize(
134                                 R.dimen.locale_picker_dialog_message_padding_left_right), 0,
135                         context.getResources().getDimensionPixelSize(
136                                 R.dimen.locale_picker_dialog_message_padding_left_right),
137                         context.getResources().getDimensionPixelSize(
138                                 R.dimen.locale_picker_dialog_message_padding_bottom))
139                 .setPositiveButton(dialogContent.mPositiveButton,
140                         view -> {
141                             controller.onClick(dialogHelper.getDialog(),
142                                     DialogInterface.BUTTON_POSITIVE);
143                             dialogHelper.getDialog().dismiss();
144                         });
145         if (dialogContent.mNegativeButton != 0) {
146             dialogHelper.setBackButton(dialogContent.mNegativeButton, view -> {
147                 controller.onClick(dialogHelper.getDialog(), DialogInterface.BUTTON_NEGATIVE);
148                 dialogHelper.getDialog().dismiss();
149             });
150         }
151         return dialogHelper.getDialog();
152     }
153 
154     @VisibleForTesting
getBackInvokedCallback()155     public OnBackInvokedCallback getBackInvokedCallback() {
156         return mBackCallback;
157     }
158 
159     @VisibleForTesting
setBackDispatcher(OnBackInvokedDispatcher dispatcher)160     public void setBackDispatcher(OnBackInvokedDispatcher dispatcher) {
161         mBackDispatcher = dispatcher;
162     }
163 
164     @VisibleForTesting
getOnBackInvokedDispatcher(@onNull Dialog dialog)165     public @NonNull OnBackInvokedDispatcher getOnBackInvokedDispatcher(@NonNull Dialog dialog) {
166         if (mBackDispatcher != null) {
167             return mBackDispatcher;
168         } else {
169             return dialog.getOnBackInvokedDispatcher();
170         }
171     }
172 
173     @VisibleForTesting
getLocaleDialogController(Context context, LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment)174     LocaleDialogController getLocaleDialogController(Context context,
175             LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) {
176         return new LocaleDialogController(context, dialogFragment, parentFragment);
177     }
178 
179     class LocaleDialogController implements DialogInterface.OnClickListener {
180         private final Context mContext;
181         private final int mDialogType;
182         private final LocaleStore.LocaleInfo mLocaleInfo;
183         private final MetricsFeatureProvider mMetricsFeatureProvider;
184         private final boolean mShowDialogForNotTranslated;
185 
186         private LocaleListEditor mParent;
187 
LocaleDialogController( @onNull Context context, @NonNull LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment)188         LocaleDialogController(
189                 @NonNull Context context, @NonNull LocaleDialogFragment dialogFragment,
190                 LocaleListEditor parentFragment) {
191             mContext = context;
192             Bundle arguments = dialogFragment.getArguments();
193             mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
194             mShowDialogForNotTranslated = arguments.getBoolean(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED);
195             mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
196             mMetricsFeatureProvider =
197                     FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
198             mParent = parentFragment;
199         }
200 
201         @Override
onClick(DialogInterface dialog, int which)202         public void onClick(DialogInterface dialog, int which) {
203             if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT
204                     || mDialogType == DIALOG_ADD_SYSTEM_LOCALE) {
205                 int result = Activity.RESULT_CANCELED;
206                 boolean changed = false;
207                 if (which == DialogInterface.BUTTON_POSITIVE) {
208                     result = Activity.RESULT_OK;
209                     changed = true;
210                 }
211                 Intent intent = new Intent();
212                 Bundle bundle = new Bundle();
213                 bundle.putInt(ARG_DIALOG_TYPE, mDialogType);
214                 bundle.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, mLocaleInfo);
215                 intent.putExtras(bundle);
216                 intent.putExtra(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, mShowDialogForNotTranslated);
217                 mParent.onActivityResult(mDialogType, result, intent);
218                 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE,
219                         changed);
220             }
221             mShouldKeepDialog = false;
222         }
223 
224         @VisibleForTesting
getDialogContent()225         DialogContent getDialogContent() {
226             DialogContent dialogContent = new DialogContent();
227             switch (mDialogType) {
228                 case DIALOG_CONFIRM_SYSTEM_DEFAULT:
229                     dialogContent.mTitle = String.format(mContext.getString(
230                             R.string.title_change_system_locale), mLocaleInfo.getFullNameNative());
231                     dialogContent.mMessage = mContext.getString(
232                             R.string.desc_notice_device_locale_settings_change);
233                     dialogContent.mPositiveButton =
234                             R.string.button_label_confirmation_of_system_locale_change;
235                     dialogContent.mNegativeButton = R.string.cancel;
236                     break;
237                 case DIALOG_NOT_AVAILABLE_LOCALE:
238                     dialogContent.mTitle = String.format(mContext.getString(
239                             R.string.title_unavailable_locale), mLocaleInfo.getFullNameNative());
240                     dialogContent.mMessage = mContext.getString(R.string.desc_unavailable_locale);
241                     dialogContent.mPositiveButton = R.string.okay;
242                     break;
243                 case DIALOG_ADD_SYSTEM_LOCALE:
244                     dialogContent.mTitle = String.format(mContext.getString(
245                                     R.string.title_system_locale_addition),
246                             mLocaleInfo.getFullNameNative());
247                     dialogContent.mMessage = mContext.getString(
248                             R.string.desc_system_locale_addition);
249                     dialogContent.mPositiveButton = R.string.add;
250                     dialogContent.mNegativeButton = R.string.cancel;
251                     break;
252                 default:
253                     break;
254             }
255             return dialogContent;
256         }
257 
258         @VisibleForTesting
259         static class DialogContent {
260             String mTitle = "";
261             String mMessage = "";
262             int mPositiveButton = 0;
263             int mNegativeButton = 0;
264         }
265     }
266 }
267