Implement LocaleNotification in Settings

Bug: 248514263
Test: atest LocaleListEditorTest
Change-Id: I8c5764997d1622f0885d5d32124a49759e585e42
This commit is contained in:
Allen Su
2023-05-12 13:15:45 +00:00
parent d134502801
commit b77c13948d
5 changed files with 331 additions and 3 deletions

View File

@@ -18,10 +18,13 @@ package com.android.settings.localepicker;
import android.app.FragmentTransaction;
import android.app.LocaleManager;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
@@ -29,6 +32,9 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.ListView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import com.android.internal.app.LocalePickerWithRegion;
import com.android.internal.app.LocaleStore;
import com.android.settings.R;
@@ -36,9 +42,14 @@ import com.android.settings.applications.AppLocaleUtil;
import com.android.settings.applications.appinfo.AppLocaleDetails;
import com.android.settings.core.SettingsBaseActivity;
import java.util.Locale;
public class AppLocalePickerActivity extends SettingsBaseActivity
implements LocalePickerWithRegion.LocaleSelectedListener, MenuItem.OnActionExpandListener {
private static final String TAG = AppLocalePickerActivity.class.getSimpleName();
static final String EXTRA_APP_LOCALE = "app_locale";
private static final String PROP_SYSTEM_LOCALE_SUGGESTION = "android.system.locale.suggestion";
private static final boolean ENABLED = false;
private String mPackageName;
private LocalePickerWithRegion mLocalePickerWithRegion;
@@ -98,6 +109,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity
setAppDefaultLocale("");
} else {
setAppDefaultLocale(localeInfo.getLocale().toLanguageTag());
broadcastAppLocaleChange(localeInfo);
}
finish();
}
@@ -125,6 +137,58 @@ public class AppLocalePickerActivity extends SettingsBaseActivity
localeManager.setApplicationLocales(mPackageName, LocaleList.forLanguageTags(languageTag));
}
private void broadcastAppLocaleChange(LocaleStore.LocaleInfo localeInfo) {
if (!SystemProperties.getBoolean(PROP_SYSTEM_LOCALE_SUGGESTION, ENABLED)) {
return;
}
String languageTag = localeInfo.getLocale().toLanguageTag();
if (isInSystemLocale(languageTag) || localeInfo.isAppCurrentLocale()) {
return;
}
String intentAction = getString(R.string.config_app_locale_intent_action);
if (!TextUtils.isEmpty(intentAction)) {
try {
PackageManager packageManager = getPackageManager();
ApplicationInfo info = packageManager.getApplicationInfo(mPackageName,
PackageManager.GET_META_DATA);
Intent intent = new Intent(intentAction)
.putExtra(Intent.EXTRA_UID, info.uid)
.putExtra(EXTRA_APP_LOCALE, languageTag);
if (intent.resolveActivity(packageManager) != null) {
mStartForResult.launch(intent);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to find info for package: " + mPackageName);
}
}
}
// Invoke startActivityFroResult so that the calling package can be shared via the intent.
private ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
}
);
/**
* Checks if the localeTag is in the system locale. Since in the current design, the system
* language list would not show two locales with the same language and region but different
* numbering system. So, during the comparison, the extension has to be stripped.
*
* @param languageTag A language tag
* @return true if the locale is in the system locale. Otherwise, false.
*/
private static boolean isInSystemLocale(String languageTag) {
LocaleList systemLocales = LocaleList.getDefault();
Locale locale = Locale.forLanguageTag(languageTag).stripExtensions();
for (int i = 0; i < systemLocales.size(); i++) {
if (locale.equals(systemLocales.get(i).stripExtensions())) {
return true;
}
}
return false;
}
private View launchAppLocaleDetailsPage() {
FrameLayout appLocaleDetailsContainer = new FrameLayout(this);
appLocaleDetailsContainer.setId(R.id.layout_app_locale_details);
@@ -171,4 +235,4 @@ public class AppLocalePickerActivity extends SettingsBaseActivity
return false;
}
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.localepicker;
import static android.os.UserManager.DISALLOW_CONFIG_LOCALE;
import static com.android.settings.localepicker.AppLocalePickerActivity.EXTRA_APP_LOCALE;
import static com.android.settings.localepicker.LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT;
import android.app.Activity;
@@ -30,6 +31,7 @@ import android.os.Bundle;
import android.os.LocaleList;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -57,6 +59,7 @@ import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.LayoutPreference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -65,10 +68,11 @@ import java.util.Locale;
*/
@SearchIndexable
public class LocaleListEditor extends RestrictedSettingsFragment implements View.OnTouchListener {
private static final String TAG = LocaleListEditor.class.getSimpleName();
protected static final String INTENT_LOCALE_KEY = "localeInfo";
private static final String CFGKEY_REMOVE_MODE = "localeRemoveMode";
private static final String CFGKEY_REMOVE_DIALOG = "showingLocaleRemoveDialog";
private static final String CFGKEY_ADD_LOCALE = "localeAdded";
private static final int MENU_ID_REMOVE = Menu.FIRST + 1;
private static final int REQUEST_LOCALE_PICKER = 0;
@@ -76,12 +80,16 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
private static final String KEY_LANGUAGES_PICKER = "languages_picker";
private static final String TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT = "dialog_confirm_system_default";
private static final String TAG_DIALOG_NOT_AVAILABLE = "dialog_not_available_locale";
static final String EXTRA_SYSTEM_LOCALE_DIALOG_TYPE = "system_locale_dialog_type";
private static final String LOCALE_SUGGESTION = "locale_suggestion";
private LocaleDragAndDropAdapter mAdapter;
private Menu mMenu;
private View mAddLanguage;
private AlertDialog mSuggestionDialog = null;
private boolean mRemoveMode;
private boolean mShowingRemoveDialog;
private boolean mLocaleAdditionMode = false;
private boolean mIsUiRestricted;
private LayoutPreference mLocalePickerPreference;
@@ -141,12 +149,21 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
}
}
@Override
public void onStop() {
super.onStop();
if (mSuggestionDialog != null) {
mSuggestionDialog.dismiss();
}
}
@Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
mRemoveMode = savedInstanceState.getBoolean(CFGKEY_REMOVE_MODE, false);
mShowingRemoveDialog = savedInstanceState.getBoolean(CFGKEY_REMOVE_DIALOG, false);
mLocaleAdditionMode = savedInstanceState.getBoolean(CFGKEY_ADD_LOCALE, false);
}
setRemoveMode(mRemoveMode);
@@ -162,6 +179,10 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
if (mShowingRemoveDialog) {
showRemoveLocaleWarningDialog();
}
if (shouldShowConfirmationDialog() && !mLocaleAdditionMode) {
getActivity().setResult(Activity.RESULT_OK);
showDialogForAddedLocale();
}
}
@Override
@@ -169,6 +190,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
super.onSaveInstanceState(outState);
outState.putBoolean(CFGKEY_REMOVE_MODE, mRemoveMode);
outState.putBoolean(CFGKEY_REMOVE_DIALOG, mShowingRemoveDialog);
outState.putBoolean(CFGKEY_ADD_LOCALE, mLocaleAdditionMode);
mAdapter.saveState(outState);
}
@@ -248,6 +270,118 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
updateVisibilityOfRemoveMenu();
}
private boolean shouldShowConfirmationDialog() {
Intent intent = this.getIntent();
String dialogType = intent.getStringExtra(EXTRA_SYSTEM_LOCALE_DIALOG_TYPE);
String localeTag = intent.getStringExtra(EXTRA_APP_LOCALE);
if (!isAllowedPackage()
|| isNullOrEmpty(dialogType)
|| isNullOrEmpty(localeTag)
|| !LOCALE_SUGGESTION.equals(dialogType)
|| !isValidLocale(localeTag)
|| isInSystemLocale(localeTag)) {
getActivity().setResult(Activity.RESULT_CANCELED);
return false;
}
getActivity().setResult(Activity.RESULT_OK);
return true;
}
private boolean isAllowedPackage() {
List<String> allowList = Arrays.asList(getContext().getResources().getStringArray(
R.array.allowed_packages_for_locale_confirmation_diallog));
String callingPckage = getActivity().getCallingPackage();
return !isNullOrEmpty(callingPckage) && allowList.contains(callingPckage);
}
private static boolean isNullOrEmpty(String str) {
return str == null || str.isEmpty();
}
private boolean isValidLocale(String tag) {
String[] systemLocales = getSupportedLocales();
for (String systemTag : systemLocales) {
if (systemTag.equals(tag)) {
return true;
}
}
return false;
}
protected String[] getSupportedLocales() {
return LocalePicker.getSupportedLocales(getContext());
}
/**
* Check if the localeTag is in the system locale. Since in the current design, the system
* language list would not show two locales with the same language and region but different
* numbering system. So, during the comparison, the u extension has to be stripped out.
*
* @param languageTag A language tag
* @return true if the locale is in the system locale. Otherwise, false.
*/
private boolean isInSystemLocale(String languageTag) {
LocaleList systemLocales = LocaleList.getDefault();
Locale locale = Locale.forLanguageTag(languageTag).stripExtensions();
for (int i = 0; i < systemLocales.size(); i++) {
if (systemLocales.get(i).stripExtensions().equals(locale)) {
return true;
}
}
return false;
}
private void showDialogForAddedLocale() {
Intent intent = this.getIntent();
String dialogType = intent.getStringExtra(EXTRA_SYSTEM_LOCALE_DIALOG_TYPE);
String appLocaleTag = intent.getStringExtra(EXTRA_APP_LOCALE);
Log.d(TAG, "Dialog suggested locale: " + appLocaleTag);
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(
Locale.forLanguageTag(appLocaleTag));
if (LOCALE_SUGGESTION.equals(dialogType)) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
customizeLayout(dialogBuilder, localeInfo.getFullNameNative());
dialogBuilder
.setPositiveButton(R.string.add, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mLocaleAdditionMode = true;
String preferencesTags = Settings.System.getString(
getContext().getContentResolver(),
Settings.System.LOCALE_PREFERENCES);
mAdapter.addLocale(mayAppendUnicodeTags(localeInfo, preferencesTags));
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mLocaleAdditionMode = true;
}
});
mSuggestionDialog = dialogBuilder.create();
mSuggestionDialog.show();
} else {
Log.d(TAG, "Invalid parameter, dialogType:" + dialogType);
}
}
private void customizeLayout(AlertDialog.Builder dialogBuilder, String language) {
View dialogView = getLocaleDialogView();
dialogBuilder.setView(dialogView);
TextView title = dialogView.findViewById(R.id.dialog_title);
title.setText(
String.format(getContext().getResources().getString(
R.string.title_system_locale_addition), language));
TextView message = dialogView.findViewById(R.id.dialog_msg);
message.setText(R.string.desc_system_locale_addition);
}
protected View getLocaleDialogView() {
LayoutInflater inflater = this.getLayoutInflater();
return inflater.inflate(R.layout.locale_dialog, null);
}
// Show the appropriate warning when the user tries to remove locales.
// Shows no warning if there is no locale checked, shows a warning
// about removing all the locales if all of them are checked, and