• 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.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.TextView;
33 import android.window.OnBackInvokedCallback;
34 import android.window.OnBackInvokedDispatcher;
35 
36 import androidx.annotation.NonNull;
37 import androidx.annotation.VisibleForTesting;
38 import androidx.appcompat.app.AlertDialog;
39 
40 import com.android.internal.app.LocaleStore;
41 import com.android.settings.R;
42 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
43 import com.android.settings.overlay.FeatureFactory;
44 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
45 
46 /**
47  * Create a dialog for system locale events.
48  */
49 public class LocaleDialogFragment extends InstrumentedDialogFragment {
50     private static final String TAG = LocaleDialogFragment.class.getSimpleName();
51 
52     static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 1;
53     static final int DIALOG_NOT_AVAILABLE_LOCALE = 2;
54 
55     static final String ARG_DIALOG_TYPE = "arg_dialog_type";
56     static final String ARG_TARGET_LOCALE = "arg_target_locale";
57     static final String ARG_SHOW_DIALOG = "arg_show_dialog";
58 
59     private boolean mShouldKeepDialog;
60     private AlertDialog mAlertDialog;
61     private OnBackInvokedDispatcher mBackDispatcher;
62 
63     private OnBackInvokedCallback mBackCallback = () -> {
64         Log.d(TAG, "Do not back to previous page if the dialog is displaying.");
65     };
66 
newInstance()67     public static LocaleDialogFragment newInstance() {
68         return new LocaleDialogFragment();
69     }
70 
71     @Override
getMetricsCategory()72     public int getMetricsCategory() {
73         int dialogType = getArguments().getInt(ARG_DIALOG_TYPE);
74         switch (dialogType) {
75             case DIALOG_CONFIRM_SYSTEM_DEFAULT:
76                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
77             case DIALOG_NOT_AVAILABLE_LOCALE:
78                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE;
79             default:
80                 return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
81         }
82     }
83 
84     @Override
onSaveInstanceState(Bundle outState)85     public void onSaveInstanceState(Bundle outState) {
86         super.onSaveInstanceState(outState);
87         outState.putBoolean(ARG_SHOW_DIALOG, mShouldKeepDialog);
88     }
89 
90     @Override
onCreateDialog(Bundle savedInstanceState)91     public Dialog onCreateDialog(Bundle savedInstanceState) {
92         if (savedInstanceState != null) {
93             Bundle arguments = getArguments();
94             int type = arguments.getInt(ARG_DIALOG_TYPE);
95             mShouldKeepDialog = savedInstanceState.getBoolean(ARG_SHOW_DIALOG, false);
96             // Keep the dialog if user rotates the device, otherwise close the confirm system
97             // default dialog only when user changes the locale.
98             if (type == DIALOG_CONFIRM_SYSTEM_DEFAULT && !mShouldKeepDialog) {
99                 dismiss();
100             }
101         }
102 
103         mShouldKeepDialog = true;
104         LocaleListEditor parentFragment = (LocaleListEditor) getParentFragment();
105         LocaleDialogController controller = getLocaleDialogController(getContext(), this,
106                 parentFragment);
107         LocaleDialogController.DialogContent dialogContent = controller.getDialogContent();
108         ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(getContext()).inflate(
109                 R.layout.locale_dialog, null);
110         setDialogTitle(viewGroup, dialogContent.mTitle);
111         setDialogMessage(viewGroup, dialogContent.mMessage);
112 
113         AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
114                 .setView(viewGroup);
115         if (!dialogContent.mPositiveButton.isEmpty()) {
116             builder.setPositiveButton(dialogContent.mPositiveButton, controller);
117         }
118         if (!dialogContent.mNegativeButton.isEmpty()) {
119             builder.setNegativeButton(dialogContent.mNegativeButton, controller);
120         }
121         mAlertDialog = builder.create();
122         getOnBackInvokedDispatcher().registerOnBackInvokedCallback(PRIORITY_DEFAULT, mBackCallback);
123         mAlertDialog.setCanceledOnTouchOutside(false);
124         mAlertDialog.setOnDismissListener(dialogInterface -> {
125             mAlertDialog.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(
126                             mBackCallback);
127         });
128 
129         return mAlertDialog;
130     }
131 
setDialogTitle(View root, String content)132     private static void setDialogTitle(View root, String content) {
133         TextView titleView = root.findViewById(R.id.dialog_title);
134         if (titleView == null) {
135             return;
136         }
137         titleView.setText(content);
138     }
139 
setDialogMessage(View root, String content)140     private static void setDialogMessage(View root, String content) {
141         TextView textView = root.findViewById(R.id.dialog_msg);
142         if (textView == null) {
143             return;
144         }
145         textView.setText(content);
146     }
147 
148     @VisibleForTesting
getBackInvokedCallback()149     public OnBackInvokedCallback getBackInvokedCallback() {
150         return mBackCallback;
151     }
152 
153     @VisibleForTesting
setBackDispatcher(OnBackInvokedDispatcher dispatcher)154     public void setBackDispatcher(OnBackInvokedDispatcher dispatcher) {
155         mBackDispatcher = dispatcher;
156     }
157 
158     @VisibleForTesting
getOnBackInvokedDispatcher()159     public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
160         if (mBackDispatcher != null) {
161             return mBackDispatcher;
162         } else {
163             return mAlertDialog.getOnBackInvokedDispatcher();
164         }
165     }
166 
167     @VisibleForTesting
getLocaleDialogController(Context context, LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment)168     LocaleDialogController getLocaleDialogController(Context context,
169             LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) {
170         return new LocaleDialogController(context, dialogFragment, parentFragment);
171     }
172 
173     class LocaleDialogController implements DialogInterface.OnClickListener {
174         private final Context mContext;
175         private final int mDialogType;
176         private final LocaleStore.LocaleInfo mLocaleInfo;
177         private final MetricsFeatureProvider mMetricsFeatureProvider;
178 
179         private LocaleListEditor mParent;
180 
LocaleDialogController( @onNull Context context, @NonNull LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment)181         LocaleDialogController(
182                 @NonNull Context context, @NonNull LocaleDialogFragment dialogFragment,
183                 LocaleListEditor parentFragment) {
184             mContext = context;
185             Bundle arguments = dialogFragment.getArguments();
186             mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
187             mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
188             mMetricsFeatureProvider = FeatureFactory.getFactory(
189                     mContext).getMetricsFeatureProvider();
190             mParent = parentFragment;
191         }
192 
193         @Override
onClick(DialogInterface dialog, int which)194         public void onClick(DialogInterface dialog, int which) {
195             if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
196                 int result = Activity.RESULT_CANCELED;
197                 boolean changed = false;
198                 if (which == DialogInterface.BUTTON_POSITIVE) {
199                     result = Activity.RESULT_OK;
200                     changed = true;
201                 }
202                 Intent intent = new Intent();
203                 Bundle bundle = new Bundle();
204                 bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
205                 intent.putExtras(bundle);
206                 mParent.onActivityResult(DIALOG_CONFIRM_SYSTEM_DEFAULT, result, intent);
207             }
208             mShouldKeepDialog = false;
209         }
210 
211         @VisibleForTesting
getDialogContent()212         DialogContent getDialogContent() {
213             DialogContent dialogContent = new DialogContent();
214             switch (mDialogType) {
215                 case DIALOG_CONFIRM_SYSTEM_DEFAULT:
216                     dialogContent.mTitle = String.format(mContext.getString(
217                             R.string.title_change_system_locale), mLocaleInfo.getFullNameNative());
218                     dialogContent.mMessage = mContext.getString(
219                             R.string.desc_notice_device_locale_settings_change);
220                     dialogContent.mPositiveButton = mContext.getString(
221                             R.string.button_label_confirmation_of_system_locale_change);
222                     dialogContent.mNegativeButton = mContext.getString(R.string.cancel);
223                     break;
224                 case DIALOG_NOT_AVAILABLE_LOCALE:
225                     dialogContent.mTitle = String.format(mContext.getString(
226                             R.string.title_unavailable_locale), mLocaleInfo.getFullNameNative());
227                     dialogContent.mMessage = mContext.getString(R.string.desc_unavailable_locale);
228                     dialogContent.mPositiveButton = mContext.getString(R.string.okay);
229                     break;
230                 default:
231                     break;
232             }
233             return dialogContent;
234         }
235 
236         @VisibleForTesting
237         static class DialogContent {
238             String mTitle = "";
239             String mMessage = "";
240             String mPositiveButton = "";
241             String mNegativeButton = "";
242         }
243     }
244 }
245