| /* |
| * Copyright (C) 2023 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.localepicker; |
| |
| import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT; |
| |
| import android.app.Activity; |
| import android.app.Dialog; |
| import android.app.settings.SettingsEnums; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.TextView; |
| import android.window.OnBackInvokedCallback; |
| import android.window.OnBackInvokedDispatcher; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.appcompat.app.AlertDialog; |
| |
| import com.android.internal.app.LocaleStore; |
| import com.android.settings.R; |
| import com.android.settings.core.instrumentation.InstrumentedDialogFragment; |
| import com.android.settings.overlay.FeatureFactory; |
| import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; |
| |
| /** |
| * Create a dialog for system locale events. |
| */ |
| public class LocaleDialogFragment extends InstrumentedDialogFragment { |
| private static final String TAG = LocaleDialogFragment.class.getSimpleName(); |
| |
| static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 1; |
| static final int DIALOG_NOT_AVAILABLE_LOCALE = 2; |
| static final int DIALOG_ADD_SYSTEM_LOCALE = 3; |
| |
| static final String ARG_DIALOG_TYPE = "arg_dialog_type"; |
| static final String ARG_TARGET_LOCALE = "arg_target_locale"; |
| static final String ARG_SHOW_DIALOG = "arg_show_dialog"; |
| |
| private boolean mShouldKeepDialog; |
| private AlertDialog mAlertDialog; |
| private OnBackInvokedDispatcher mBackDispatcher; |
| |
| private OnBackInvokedCallback mBackCallback = () -> { |
| Log.d(TAG, "Do not back to previous page if the dialog is displaying."); |
| }; |
| |
| public static LocaleDialogFragment newInstance() { |
| return new LocaleDialogFragment(); |
| } |
| |
| @Override |
| public int getMetricsCategory() { |
| int dialogType = getArguments().getInt(ARG_DIALOG_TYPE); |
| switch (dialogType) { |
| case DIALOG_CONFIRM_SYSTEM_DEFAULT: |
| return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE; |
| case DIALOG_NOT_AVAILABLE_LOCALE: |
| return SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE; |
| default: |
| return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE; |
| } |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putBoolean(ARG_SHOW_DIALOG, mShouldKeepDialog); |
| } |
| |
| @Override |
| public Dialog onCreateDialog(Bundle savedInstanceState) { |
| if (savedInstanceState != null) { |
| Bundle arguments = getArguments(); |
| int type = arguments.getInt(ARG_DIALOG_TYPE); |
| mShouldKeepDialog = savedInstanceState.getBoolean(ARG_SHOW_DIALOG, false); |
| // Keep the dialog if user rotates the device, otherwise close the confirm system |
| // default dialog only when user changes the locale. |
| if ((type == DIALOG_CONFIRM_SYSTEM_DEFAULT || type == DIALOG_ADD_SYSTEM_LOCALE) |
| && !mShouldKeepDialog) { |
| dismiss(); |
| } |
| } |
| |
| mShouldKeepDialog = true; |
| LocaleListEditor parentFragment = (LocaleListEditor) getParentFragment(); |
| LocaleDialogController controller = getLocaleDialogController(getContext(), this, |
| parentFragment); |
| LocaleDialogController.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); |
| } |
| mAlertDialog = builder.create(); |
| getOnBackInvokedDispatcher().registerOnBackInvokedCallback(PRIORITY_DEFAULT, mBackCallback); |
| mAlertDialog.setCanceledOnTouchOutside(false); |
| mAlertDialog.setOnDismissListener(dialogInterface -> { |
| mAlertDialog.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback( |
| mBackCallback); |
| }); |
| |
| return mAlertDialog; |
| } |
| |
| 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 |
| public OnBackInvokedCallback getBackInvokedCallback() { |
| return mBackCallback; |
| } |
| |
| @VisibleForTesting |
| public void setBackDispatcher(OnBackInvokedDispatcher dispatcher) { |
| mBackDispatcher = dispatcher; |
| } |
| |
| @VisibleForTesting |
| public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { |
| if (mBackDispatcher != null) { |
| return mBackDispatcher; |
| } else { |
| return mAlertDialog.getOnBackInvokedDispatcher(); |
| } |
| } |
| |
| @VisibleForTesting |
| LocaleDialogController getLocaleDialogController(Context context, |
| LocaleDialogFragment dialogFragment, LocaleListEditor parentFragment) { |
| return new LocaleDialogController(context, dialogFragment, parentFragment); |
| } |
| |
| class LocaleDialogController implements DialogInterface.OnClickListener { |
| private final Context mContext; |
| private final int mDialogType; |
| private final LocaleStore.LocaleInfo mLocaleInfo; |
| private final MetricsFeatureProvider mMetricsFeatureProvider; |
| |
| private LocaleListEditor mParent; |
| |
| LocaleDialogController( |
| @NonNull Context context, @NonNull LocaleDialogFragment dialogFragment, |
| LocaleListEditor parentFragment) { |
| mContext = context; |
| Bundle arguments = dialogFragment.getArguments(); |
| mDialogType = arguments.getInt(ARG_DIALOG_TYPE); |
| mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE); |
| mMetricsFeatureProvider = |
| FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); |
| mParent = parentFragment; |
| } |
| |
| @Override |
| public void onClick(DialogInterface dialog, int which) { |
| if (mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT |
| || mDialogType == DIALOG_ADD_SYSTEM_LOCALE) { |
| int result = Activity.RESULT_CANCELED; |
| boolean changed = false; |
| if (which == DialogInterface.BUTTON_POSITIVE) { |
| result = Activity.RESULT_OK; |
| changed = true; |
| } |
| Intent intent = new Intent(); |
| Bundle bundle = new Bundle(); |
| bundle.putInt(ARG_DIALOG_TYPE, mDialogType); |
| bundle.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, mLocaleInfo); |
| intent.putExtras(bundle); |
| mParent.onActivityResult(mDialogType, result, intent); |
| mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE, |
| changed); |
| } |
| mShouldKeepDialog = false; |
| } |
| |
| @VisibleForTesting |
| DialogContent getDialogContent() { |
| DialogContent dialogContent = new DialogContent(); |
| switch (mDialogType) { |
| case DIALOG_CONFIRM_SYSTEM_DEFAULT: |
| dialogContent.mTitle = String.format(mContext.getString( |
| R.string.title_change_system_locale), mLocaleInfo.getFullNameNative()); |
| dialogContent.mMessage = mContext.getString( |
| R.string.desc_notice_device_locale_settings_change); |
| dialogContent.mPositiveButton = mContext.getString( |
| R.string.button_label_confirmation_of_system_locale_change); |
| dialogContent.mNegativeButton = mContext.getString(R.string.cancel); |
| break; |
| case DIALOG_NOT_AVAILABLE_LOCALE: |
| dialogContent.mTitle = String.format(mContext.getString( |
| R.string.title_unavailable_locale), mLocaleInfo.getFullNameNative()); |
| dialogContent.mMessage = mContext.getString(R.string.desc_unavailable_locale); |
| dialogContent.mPositiveButton = mContext.getString(R.string.okay); |
| break; |
| case DIALOG_ADD_SYSTEM_LOCALE: |
| dialogContent.mTitle = String.format(mContext.getString( |
| R.string.title_system_locale_addition), |
| mLocaleInfo.getFullNameNative()); |
| dialogContent.mMessage = mContext.getString( |
| R.string.desc_system_locale_addition); |
| dialogContent.mPositiveButton = mContext.getString(R.string.add); |
| dialogContent.mNegativeButton = mContext.getString(R.string.cancel); |
| break; |
| default: |
| break; |
| } |
| return dialogContent; |
| } |
| |
| @VisibleForTesting |
| static class DialogContent { |
| String mTitle = ""; |
| String mMessage = ""; |
| String mPositiveButton = ""; |
| String mNegativeButton = ""; |
| } |
| } |
| } |