From 9be043e750940cae72c8780b4dbd6d66c8719442 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Tue, 8 Mar 2022 17:40:16 +0800 Subject: [PATCH] [Panlingual] Revamp the panlingual UI in Settings. - Create a Activity to contain AppLocaleDetail and LocalePickerWithRegion - Update the Entry from apps language page Bug: 223089715 Test: local test pass Change-Id: Id01e93f3df32412c7323ca577a149009eb1862ad Merged-In: Id01e93f3df32412c7323ca577a149009eb1862ad --- AndroidManifest.xml | 4 +- res/layout/app_locale_picker.xml | 33 ++ res/xml/app_locale_details.xml | 16 +- src/com/android/settings/Settings.java | 2 - .../appinfo/AppLocaleDetails.java | 403 +++++----------- .../ManageApplications.java | 6 +- .../localepicker/AppLocalePickerActivity.java | 100 ++++ .../LocalePickerWithRegionActivity.java | 34 +- .../appinfo/AppLocaleDetailsTest.java | 432 ------------------ 9 files changed, 273 insertions(+), 757 deletions(-) create mode 100644 res/layout/app_locale_picker.xml create mode 100644 src/com/android/settings/localepicker/AppLocalePickerActivity.java delete mode 100644 tests/unit/src/com/android/settings/applications/appinfo/AppLocaleDetailsTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 214d32a3c17..70266b655bd 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -851,7 +851,7 @@ @@ -859,8 +859,6 @@ - + + + + + + + + + \ No newline at end of file diff --git a/res/xml/app_locale_details.xml b/res/xml/app_locale_details.xml index 40ca58243ec..05e72ee7c9c 100644 --- a/res/xml/app_locale_details.xml +++ b/res/xml/app_locale_details.xml @@ -18,6 +18,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/app_locale_picker_title"> + - - - - - - - - diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 57d7d105188..10a47718e6f 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -108,8 +108,6 @@ public class Settings extends SettingsActivity { public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ } public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ } public static class LocalePickerActivity extends SettingsActivity { /* empty */ } - /** Activity for the App locale details settings. */ - public static class AppLocalePickerActivity extends SettingsActivity { /* empty */ } public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ } public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } public static class DarkThemeSettingsActivity extends SettingsActivity { /* empty */ } diff --git a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java index d50a67b8305..5f75b6b3a6d 100644 --- a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java +++ b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java @@ -22,73 +22,64 @@ import android.app.LocaleConfig; import android.app.LocaleManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.res.Resources; import android.os.Bundle; import android.os.LocaleList; -import android.telephony.TelephonyManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.RadioButtonPreference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; import java.util.Locale; /** - * A fragment to show the current app locale info and help the user to select the expected locale. + * TODO(b/223503670): Implement the unittest. + * A fragment to show the current app locale info. */ -public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreference.OnClickListener { +public class AppLocaleDetails extends SettingsPreferenceFragment { private static final String TAG = "AppLocaleDetails"; - private static final String CATEGORY_KEY_SUGGESTED_LANGUAGES = - "category_key_suggested_languages"; - private static final String CATEGORY_KEY_ALL_LANGUAGES = - "category_key_all_languages"; private static final String KEY_APP_DESCRIPTION = "app_locale_description"; - @VisibleForTesting - static final String KEY_SYSTEM_DEFAULT_LOCALE = "system_default_locale"; private boolean mCreated = false; - @VisibleForTesting - AppLocaleDetailsHelper mAppLocaleDetailsHelper; - - private PreferenceGroup mGroupOfSuggestedLocales; - private PreferenceGroup mGroupOfSupportedLocales; + private String mPackageName; private LayoutPreference mPrefOfDescription; - private RadioButtonPreference mDefaultPreference; + + /** + * Create a instance of AppLocaleDetails. + * @param packageName Indicates which application need to show the locale picker. + */ + public static AppLocaleDetails newInstance(String packageName) { + AppLocaleDetails appLocaleDetails = new AppLocaleDetails(); + Bundle bundle = new Bundle(); + bundle.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName); + appLocaleDetails.setArguments(bundle); + return appLocaleDetails; + } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.app_locale_details); - mAppLocaleDetailsHelper = new AppLocaleDetailsHelper(getContext(), mPackageName); + Bundle bundle = getArguments(); + mPackageName = bundle.getString(AppInfoBase.ARG_PACKAGE_NAME, ""); - mGroupOfSuggestedLocales = - getPreferenceScreen().findPreference(CATEGORY_KEY_SUGGESTED_LANGUAGES); - mGroupOfSupportedLocales = - getPreferenceScreen().findPreference(CATEGORY_KEY_ALL_LANGUAGES); - mPrefOfDescription = getPreferenceScreen().findPreference(KEY_APP_DESCRIPTION); - - mDefaultPreference = (RadioButtonPreference) getPreferenceScreen() - .findPreference(KEY_SYSTEM_DEFAULT_LOCALE); - mDefaultPreference.setOnClickListener(this); + if (mPackageName.isEmpty()) { + Log.d(TAG, "No package name."); + finish(); + } } // Override here so we don't have an empty screen @@ -96,8 +87,8 @@ public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreferen public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // if we don't have a package info, show a page saying this is unsupported - if (mPackageInfo == null) { + // if we don't have a package, show a page saying this is unsupported + if (mPackageName.isEmpty()) { return inflater.inflate(R.layout.manage_applications_apps_unsupported, null); } return super.onCreateView(inflater, container, savedInstanceState); @@ -105,46 +96,19 @@ public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreferen @Override public void onResume() { - // Update Locales first, before refresh ui. - mAppLocaleDetailsHelper.handleAllLocalesData(); - super.onResume(); - mDefaultPreference.setSummary(Locale.getDefault().getDisplayName(Locale.getDefault())); - } - - @Override - protected boolean refreshUi() { refreshUiInternal(); - return true; + super.onResume(); } - @VisibleForTesting - void refreshUiInternal() { - if (mAppLocaleDetailsHelper.getSupportedLocales().isEmpty()) { + private void refreshUiInternal() { + if (!hasAppSupportedLocales()) { Log.d(TAG, "No supported language."); - mGroupOfSuggestedLocales.setVisible(false); - mGroupOfSupportedLocales.setVisible(false); mPrefOfDescription.setVisible(true); TextView description = (TextView) mPrefOfDescription.findViewById(R.id.description); description.setText(getContext().getString(R.string.no_multiple_language_supported, Locale.getDefault().getDisplayName(Locale.getDefault()))); return; } - resetLocalePreferences(); - Locale appLocale = AppLocaleDetailsHelper.getAppDefaultLocale(getContext(), mPackageName); - // Sets up default locale preference. - mGroupOfSuggestedLocales.addPreference(mDefaultPreference); - mDefaultPreference.setChecked(appLocale == null); - // Sets up suggested locales of per app. - setLanguagesPreference(mGroupOfSuggestedLocales, - mAppLocaleDetailsHelper.getSuggestedLocales(), appLocale); - // Sets up supported locales of per app. - setLanguagesPreference(mGroupOfSupportedLocales, - mAppLocaleDetailsHelper.getSupportedLocales(), appLocale); - } - - private void resetLocalePreferences() { - mGroupOfSuggestedLocales.removeAll(); - mGroupOfSupportedLocales.removeAll(); } @Override @@ -152,22 +116,6 @@ public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreferen return SettingsEnums.APPS_LOCALE_LIST; } - @Override - protected AlertDialog createDialog(int id, int errorCode) { - return null; - } - - @Override - public void onRadioButtonClicked(RadioButtonPreference pref) { - String key = pref.getKey(); - if (KEY_SYSTEM_DEFAULT_LOCALE.equals(key)) { - mAppLocaleDetailsHelper.setAppDefaultLocale(LocaleList.forLanguageTags("")); - } else { - mAppLocaleDetailsHelper.setAppDefaultLocale(key); - } - refreshUi(); - } - @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -176,32 +124,98 @@ public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreferen return; } mCreated = true; - if (mPackageInfo == null) { + if (mPackageName == null) { return; } // Creates a head icon button of app on this page. final Activity activity = getActivity(); + ApplicationInfo applicationInfo = + getApplicationInfo(mPackageName, getContext().getUserId()); final Preference pref = EntityHeaderController .newInstance(activity, this, null /* header */) .setRecyclerView(getListView(), getSettingsLifecycle()) - .setIcon(Utils.getBadgedIcon(getContext(), mPackageInfo.applicationInfo)) - .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) - .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) + .setIcon(Utils.getBadgedIcon(getContext(), applicationInfo)) + .setLabel(applicationInfo.loadLabel(getContext().getPackageManager())) + .setIsInstantApp(AppUtils.isInstant(applicationInfo)) .setPackageName(mPackageName) - .setUid(mPackageInfo.applicationInfo.uid) + .setUid(applicationInfo.uid) .setHasAppInfoLink(true) .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE) .done(activity, getPrefContext()); getPreferenceScreen().addPreference(pref); } + private ApplicationInfo getApplicationInfo(String packageName, int userId) { + ApplicationInfo applicationInfo; + try { + applicationInfo = getContext().getPackageManager() + .getApplicationInfoAsUser(packageName, /* flags= */ 0, userId); + return applicationInfo; + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Application info not found for: " + packageName); + return null; + } + } + + private boolean hasAppSupportedLocales() { + LocaleList localeList = getPackageLocales(); + return (localeList != null && localeList.size() > 0) || getAssetLocales().length > 0; + } + + private String[] getAssetLocales() { + try { + PackageManager packageManager = getContext().getPackageManager(); + String[] locales = packageManager.getResourcesForApplication( + packageManager.getPackageInfo(mPackageName, PackageManager.MATCH_ALL) + .applicationInfo).getAssets().getNonSystemLocales(); + if (locales == null) { + Log.i(TAG, "[" + mPackageName + "] locales are null."); + } + if (locales.length <= 0) { + Log.i(TAG, "[" + mPackageName + "] locales length is 0."); + return new String[0]; + } + String locale = locales[0]; + Log.i(TAG, "First asset locale - [" + mPackageName + "] " + locale); + return locales; + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can not found the package name : " + mPackageName + " / " + e); + } + return new String[0]; + } + + private LocaleList getPackageLocales() { + try { + LocaleConfig localeConfig = + new LocaleConfig(getContext().createPackageContext(mPackageName, 0)); + if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { + return localeConfig.getSupportedLocales(); + } + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can not found the package name : " + mPackageName + " / " + e); + } + return null; + } + + /** Gets per app's default locale */ + public static Locale getAppDefaultLocale(Context context, String packageName) { + LocaleManager localeManager = context.getSystemService(LocaleManager.class); + try { + LocaleList localeList = (localeManager == null) + ? null : localeManager.getApplicationLocales(packageName); + return localeList == null ? null : localeList.get(0); + } catch (IllegalArgumentException e) { + Log.w(TAG, "package name : " + packageName + " is not correct. " + e); + } + return null; + } + /** * TODO (b209962418) Do a performance test to low end device. * @return Return the summary to show the current app's language. */ public static CharSequence getSummary(Context context, String packageName) { - Locale appLocale = - AppLocaleDetailsHelper.getAppDefaultLocale(context, packageName); + Locale appLocale = getAppDefaultLocale(context, packageName); if (appLocale == null) { Locale systemLocale = Locale.getDefault(); return context.getString(R.string.preference_of_system_locale_summary, @@ -210,217 +224,4 @@ public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreferen return appLocale.getDisplayName(appLocale); } } - - private void setLanguagesPreference(PreferenceGroup group, - Collection locales, Locale appLocale) { - if (locales == null) { - return; - } - - for (Locale locale : locales) { - if (locale == null) { - continue; - } - - RadioButtonPreference pref = new RadioButtonPreference(getContext()); - pref.setTitle(locale.getDisplayName(locale)); - pref.setKey(locale.toLanguageTag()); - // Will never be checked if appLocale is null - // aka if there is no per-app locale - pref.setChecked(locale.equals(appLocale)); - pref.setOnClickListener(this); - group.addPreference(pref); - } - } - - @VisibleForTesting - static class AppLocaleDetailsHelper { - private String mPackageName; - private Context mContext; - private TelephonyManager mTelephonyManager; - private LocaleManager mLocaleManager; - - private Collection mProcessedSuggestedLocales = new ArrayList<>(); - private Collection mProcessedSupportedLocales = new ArrayList<>(); - - private Collection mAppSupportedLocales = new ArrayList<>(); - - AppLocaleDetailsHelper(Context context, String packageName) { - mContext = context; - mPackageName = packageName; - mTelephonyManager = context.getSystemService(TelephonyManager.class); - mLocaleManager = context.getSystemService(LocaleManager.class); - mAppSupportedLocales = getAppSupportedLocales(); - } - - /** Handle suggested and supported locales for UI display. */ - public void handleAllLocalesData() { - clearLocalesData(); - handleSuggestedLocales(); - handleSupportedLocales(); - } - - /** Gets suggested locales in the app. */ - public Collection getSuggestedLocales() { - return mProcessedSuggestedLocales; - } - - /** Gets supported locales in the app. */ - public Collection getSupportedLocales() { - return mProcessedSupportedLocales; - } - - @VisibleForTesting - void handleSuggestedLocales() { - Locale appLocale = getAppDefaultLocale(mContext, mPackageName); - // 1st locale in suggested languages group. - for (Locale supportedlocale : mAppSupportedLocales) { - if (compareLocale(supportedlocale, appLocale)) { - mProcessedSuggestedLocales.add(appLocale); - break; - } - } - - // 2nd and 3rd locale in suggested languages group. - String simCountry = mTelephonyManager.getSimCountryIso().toUpperCase(Locale.US); - String networkCountry = mTelephonyManager.getNetworkCountryIso().toUpperCase(Locale.US); - mAppSupportedLocales.forEach(supportedlocale -> { - String localeCountry = supportedlocale.getCountry().toUpperCase(Locale.US); - if (!compareLocale(supportedlocale, appLocale) - && isCountrySuggestedLocale(localeCountry, simCountry, networkCountry)) { - mProcessedSuggestedLocales.add(supportedlocale); - } - }); - - // Other locales in suggested languages group. - Collection supportedSystemLocales = new HashSet<>(); - getCurrentSystemLocales().forEach(systemLocale -> { - mAppSupportedLocales.forEach(supportedLocale -> { - if (compareLocale(systemLocale, supportedLocale)) { - supportedSystemLocales.add(supportedLocale); - } - }); - }); - supportedSystemLocales.removeAll(mProcessedSuggestedLocales); - mProcessedSuggestedLocales.addAll(supportedSystemLocales); - } - - @VisibleForTesting - static boolean compareLocale(Locale source, Locale target) { - if (source == null && target == null) { - return true; - } else if (source != null && target != null) { - return LocaleList.matchesLanguageAndScript(source, target); - } else { - return false; - } - } - - private static boolean isCountrySuggestedLocale(String localeCountry, - String simCountry, - String networkCountry) { - return ((!simCountry.isEmpty() && simCountry.equals(localeCountry)) - || (!networkCountry.isEmpty() && networkCountry.equals(localeCountry))); - } - - @VisibleForTesting - void handleSupportedLocales() { - mProcessedSupportedLocales.addAll(mAppSupportedLocales); - - if (mProcessedSuggestedLocales != null || !mProcessedSuggestedLocales.isEmpty()) { - mProcessedSuggestedLocales.retainAll(mProcessedSupportedLocales); - mProcessedSupportedLocales.removeAll(mProcessedSuggestedLocales); - } - } - - private void clearLocalesData() { - mProcessedSuggestedLocales.clear(); - mProcessedSupportedLocales.clear(); - } - - private Collection getAppSupportedLocales() { - Collection appSupportedLocales = new ArrayList<>(); - LocaleList localeList = getPackageLocales(); - - if (localeList != null && localeList.size() > 0) { - for (int i = 0; i < localeList.size(); i++) { - appSupportedLocales.add(localeList.get(i)); - } - } else { - String[] languages = getAssetLocales(); - for (String language : languages) { - appSupportedLocales.add(Locale.forLanguageTag(language)); - } - } - return appSupportedLocales; - } - - /** Gets per app's default locale */ - public static Locale getAppDefaultLocale(Context context, String packageName) { - LocaleManager localeManager = context.getSystemService(LocaleManager.class); - try { - LocaleList localeList = (localeManager == null) - ? null : localeManager.getApplicationLocales(packageName); - return localeList == null ? null : localeList.get(0); - } catch (IllegalArgumentException e) { - Log.w(TAG, "package name : " + packageName + " is not correct. " + e); - } - return null; - } - - /** Sets per app's default language to system. */ - public void setAppDefaultLocale(String languageTag) { - if (languageTag.isEmpty()) { - Log.w(TAG, "[setAppDefaultLocale] No language tag."); - return; - } - setAppDefaultLocale(LocaleList.forLanguageTags(languageTag)); - } - - /** Sets per app's default language to system. */ - public void setAppDefaultLocale(LocaleList localeList) { - if (mLocaleManager == null) { - Log.w(TAG, "LocaleManager is null, and cannot set the app locale up."); - return; - } - mLocaleManager.setApplicationLocales(mPackageName, localeList); - } - - @VisibleForTesting - Collection getCurrentSystemLocales() { - LocaleList localeList = Resources.getSystem().getConfiguration().getLocales(); - Collection systemLocales = new ArrayList<>(); - for (int i = 0; i < localeList.size(); i++) { - systemLocales.add(localeList.get(i)); - } - return systemLocales; - } - - @VisibleForTesting - String[] getAssetLocales() { - try { - PackageManager packageManager = mContext.getPackageManager(); - return packageManager.getResourcesForApplication( - packageManager.getPackageInfo(mPackageName, PackageManager.MATCH_ALL) - .applicationInfo).getAssets().getNonSystemLocales(); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Can not found the package name : " + mPackageName + " / " + e); - } - return new String[0]; - } - - @VisibleForTesting - LocaleList getPackageLocales() { - try { - LocaleConfig localeConfig = - new LocaleConfig(mContext.createPackageContext(mPackageName, 0)); - if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { - return localeConfig.getSupportedLocales(); - } - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Can not found the package name : " + mPackageName + " / " + e); - } - return null; - } - } } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index a6ce6fbeb84..b8a0d22f9b3 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -120,6 +120,7 @@ import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.profileselector.ProfileSelectFragment; import com.android.settings.fuelgauge.HighPowerDetail; +import com.android.settings.localepicker.AppLocalePickerActivity; import com.android.settings.notification.ConfigureNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.app.AppNotificationSettings; @@ -635,8 +636,9 @@ public class ManageApplications extends InstrumentedFragment R.string.media_management_apps_title); break; case LIST_TYPE_APPS_LOCALE: - startAppInfoFragment(AppLocaleDetails.class, - R.string.app_locale_picker_title); + Intent intent = new Intent(getContext(), AppLocalePickerActivity.class); + intent.setData(Uri.parse("package:" + mCurrentPkgName)); + startActivity(intent); break; // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java new file mode 100644 index 00000000000..80d3336c181 --- /dev/null +++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 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 android.app.FragmentTransaction; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.MenuItem; + +import com.android.internal.app.LocalePickerWithRegion; +import com.android.internal.app.LocaleStore; +import com.android.settings.R; +import com.android.settings.applications.appinfo.AppLocaleDetails; +import com.android.settings.core.SettingsBaseActivity; + +/** + * TODO(b/223503670): Add unit test for AppLocalePickerActivity. + * A activity to show the locale picker and information page. + */ +public class AppLocalePickerActivity extends SettingsBaseActivity + implements LocalePickerWithRegion.LocaleSelectedListener { + private static final String TAG = AppLocalePickerActivity.class.getSimpleName(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String packageName = getIntent().getData().getSchemeSpecificPart(); + if (TextUtils.isEmpty(packageName)) { + Log.d(TAG, "There is no package name."); + finish(); + return; + } + + getActionBar().setDisplayHomeAsUpEnabled(true); + setContentView(R.layout.app_locale_picker); + + // Create App locale info detail part. + AppLocaleDetails appLocaleDetails = AppLocaleDetails.newInstance(packageName); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.app_locale_detail, appLocaleDetails) + .commit(); + + // Create Locale picker part. + final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker( + this, AppLocalePickerActivity.this, false /* translate only */); + // LocalePickerWithRegion use android.app.ListFragment. Thus, it can not user + // getSupportFragmentManager() to add this into container. + getFragmentManager() + .beginTransaction() + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .replace(R.id.app_locale_picker_with_region, selector) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + handleBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + handleBackPressed(); + } + + private void handleBackPressed() { + if (getFragmentManager().getBackStackEntryCount() > 1) { + super.onBackPressed(); + } else { + setResult(RESULT_CANCELED); + finish(); + } + } + + @Override + public void onLocaleSelected(LocaleStore.LocaleInfo locale) { + // TODO When locale is selected, this shall set per app language here. + finish(); + } +} + diff --git a/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java index bcc55e3a2db..70f738395be 100644 --- a/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java +++ b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java @@ -17,8 +17,11 @@ package com.android.settings.localepicker; import android.app.FragmentTransaction; +import android.app.LocaleManager; import android.content.Intent; import android.os.Bundle; +import android.os.LocaleList; +import android.util.Log; import android.view.MenuItem; import com.android.internal.app.LocalePickerWithRegion; @@ -31,6 +34,7 @@ public class LocalePickerWithRegionActivity extends SettingsBaseActivity implements LocalePickerWithRegion.LocaleSelectedListener { private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; + private static final String TAG = "Calvin"; @Override public void onCreate(Bundle savedInstanceState) { @@ -47,6 +51,25 @@ public class LocalePickerWithRegionActivity extends SettingsBaseActivity .commit(); } + public void setAppDefaultLocale(String languageTag) { + if (languageTag.isEmpty()) { + Log.w(TAG, "[setAppDefaultLocale] No language tag."); + return; + } + setAppDefaultLocale(LocaleList.forLanguageTags(languageTag)); + } + + /** Sets per app's default language to system. */ + public void setAppDefaultLocale(LocaleList localeList) { + LocaleManager mLocaleManager = getSystemService(LocaleManager.class); + if (mLocaleManager == null) { + Log.w(TAG, "LocaleManager is null, and cannot set the app locale up."); + return; + } + mLocaleManager.setApplicationLocales("com.android.vending", localeList); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { @@ -58,9 +81,16 @@ public class LocalePickerWithRegionActivity extends SettingsBaseActivity @Override public void onLocaleSelected(LocaleStore.LocaleInfo locale) { - final Intent intent = new Intent(); + /*final Intent intent = new Intent(); intent.putExtra(LocaleListEditor.INTENT_LOCALE_KEY, locale); - setResult(RESULT_OK, intent); + setResult(RESULT_OK, intent);*/ + if(locale != null) { + Log.d("Calvin", "onLocaleSelected " + locale.getLocale().toLanguageTag()); + setAppDefaultLocale(locale.getLocale().toLanguageTag()); + } else { + Log.d("Calvin", "onLocaleSelected null"); + setAppDefaultLocale(""); + } finish(); } diff --git a/tests/unit/src/com/android/settings/applications/appinfo/AppLocaleDetailsTest.java b/tests/unit/src/com/android/settings/applications/appinfo/AppLocaleDetailsTest.java deleted file mode 100644 index aa0daad44ff..00000000000 --- a/tests/unit/src/com/android/settings/applications/appinfo/AppLocaleDetailsTest.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (C) 2021 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.applications.appinfo; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.LocaleManager; -import android.content.Context; -import android.os.LocaleList; -import android.os.Looper; -import android.telephony.TelephonyManager; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settingslib.widget.RadioButtonPreference; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; - -/** - * Unittest for ApplocaleDetails - * TODO Need to add a unittest for the UI preference component. - */ -@RunWith(AndroidJUnit4.class) -public class AppLocaleDetailsTest { - private static final String APP_PACKAGE_NAME = "app_package_name"; - - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private LocaleManager mLocaleManager; - - private Context mContext; - private Collection mSystemLocales; - private LocaleList mAppLocale; - private String[] mAssetLocales; - private LocaleList mPackageLocales; - - @Before - @UiThreadTest - public void setUp() { - MockitoAnnotations.initMocks(this); - if (Looper.myLooper() == null) { - Looper.prepare(); - } - mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); - when(mContext.getSystemService(LocaleManager.class)).thenReturn(mLocaleManager); - - setupInitialLocales( - /* appLocale= */ "en-gb", - /* simCountry= */ "tw", - /* networkCountry= */ "jp", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "pa, cn, zh-tw, en-gb, ja-jp", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "zh-tw", "ja-jp"}); - } - - @Test - @UiThreadTest - public void onRadioButtonClicked_setCurrentLocaleToSystem() { - AppLocaleDetails appLocaleDetails = new AppLocaleDetails() { - @Override - void refreshUiInternal() {} - }; - DummyAppLocaleDetailsHelper helper = - spy(new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME)); - appLocaleDetails.mAppLocaleDetailsHelper = helper; - RadioButtonPreference pref = new RadioButtonPreference(mContext); - pref.setKey(AppLocaleDetails.KEY_SYSTEM_DEFAULT_LOCALE); - - appLocaleDetails.onRadioButtonClicked(pref); - - verify(helper).setAppDefaultLocale(LocaleList.forLanguageTags("")); - } - - @Test - @UiThreadTest - public void onRadioButtonClicked_setCurrentLocaleForUserSelected() { - AppLocaleDetails appLocaleDetails = new AppLocaleDetails() { - @Override - void refreshUiInternal() {} - }; - DummyAppLocaleDetailsHelper helper = - spy(new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME)); - appLocaleDetails.mAppLocaleDetailsHelper = helper; - RadioButtonPreference pref = new RadioButtonPreference(mContext); - pref.setKey("en"); - - appLocaleDetails.onRadioButtonClicked(pref); - - verify(helper).setAppDefaultLocale("en"); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_localeManagerIsNull_noCrash() { - when(mContext.getSystemService(LocaleManager.class)).thenReturn(null); - - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_1stLocaleIsAppLocaleAndHasSimAndNetwork() { - Locale simCountryLocale = new Locale("zh", "TW"); - Locale networkCountryLocale = new Locale("ja", "JP"); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - Locale locale = suggestedLocales.iterator().next(); - assertTrue(locale.equals(mAppLocale.get(0))); - assertTrue(suggestedLocales.contains(simCountryLocale)); - assertTrue(suggestedLocales.contains(networkCountryLocale)); - } - - @Test - @UiThreadTest - public void - handleAllLocalesData_noAppAndNoSupportedSimLocale_suggestedLocaleIsSupported() { - Locale testEnAssetLocale = new Locale("en", "GB"); - Locale testJaAssetLocale = new Locale("ja", "JP"); - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "tw", - /* networkCountry= */ "", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "ja-jp"}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - assertTrue(suggestedLocales.contains(testEnAssetLocale)); - assertTrue(suggestedLocales.contains(testJaAssetLocale)); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_noAppButHasSupportedSimLocale_1stSuggestedLocaleIsSim() { - Locale simLocale = new Locale("zh", "tw"); - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "tw", - /* networkCountry= */ "", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "ja-jp", "zh-tw"}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - Locale locale = suggestedLocales.iterator().next(); - assertTrue(locale.equals(simLocale)); - } - - @Test - @UiThreadTest - public void - handleAllLocalesData_noAppButHasSupportedNetworkLocale_1stSuggestedLocaleIsNetwork() { - Locale networkLocale = new Locale("ja", "JP"); - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "", - /* networkCountry= */ "jp", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "ja-jp"}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - Locale locale = suggestedLocales.iterator().next(); - assertTrue(locale.equals(networkLocale)); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_noAppSimOrNetworkLocale_suggestedLocalesHasSystemLocale() { - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "", - /* networkCountry= */ "", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "zh-tw", "ja-jp"}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - assertTrue(suggestedLocales.contains(Locale.forLanguageTag("ne"))); - // ru language is not present in the asset locales - assertFalse(suggestedLocales.contains(Locale.forLanguageTag("ru"))); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_noAppButHasSimAndNetworkLocale_1stLocaleIsSimLocale() { - Locale simCountryLocale = new Locale("zh", "TW"); - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "tw", - /* networkCountry= */ "jp", - /* systemLocales= */ "en-gb, ru, ja-jp, ne, zh-tw", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{"en-gb", "ne", "ms", "pa", "zh-tw", "ja-jp"}); - - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - Locale locale = suggestedLocales.iterator().next(); - assertTrue(locale.equals(simCountryLocale)); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_noSupportedLocale_noSuggestedLocales() { - Locale networkCountryLocale = new Locale("en", "GB"); - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "", - /* networkCountry= */ "gb", - /* systemLocales= */ "en, uk, jp, ne", - /* packageLocales= */ "", - /* assetLocales= */ new String[]{}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - assertTrue(suggestedLocales.size() == 0); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_hasPackageAndSystemLocales_1stLocaleIs1stOneInSystemLocales() { - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "", - /* networkCountry= */ "", - /* systemLocales= */ "en, uk, jp, ne", - /* packageLocales= */ "pa, cn, tw, en", - /* assetLocales= */ new String[]{}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - Locale locale = suggestedLocales.iterator().next(); - Locale systemLocale = mSystemLocales.iterator().next(); - assertTrue(locale.equals(systemLocale)); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_sameLocaleButDifferentRegion_notShowDuplicatedLocale() { - setupInitialLocales( - /* appLocale= */ "", - /* simCountry= */ "", - /* networkCountry= */ "", - /* systemLocales= */ "en-us, en-gb, jp, ne", - /* packageLocales= */ "pa, cn, tw, en-us, en-gb", - /* assetLocales= */ new String[]{}); - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - Collection suggestedLocales = helper.getSuggestedLocales(); - assertFalse(hasDuplicatedResult(suggestedLocales)); - } - - private boolean hasDuplicatedResult(Collection locales) { - Set tempSet = new HashSet<>(); - for (Locale locale : locales) { - if (!tempSet.add(locale)) { - return true; - } - } - return false; - } - - @Test - @UiThreadTest - public void handleAllLocalesData_supportLocaleListIsNotEmpty() { - DummyAppLocaleDetailsHelper helper = - new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME); - - helper.handleAllLocalesData(); - - assertFalse(helper.getSupportedLocales().isEmpty()); - } - - @Test - @UiThreadTest - public void handleAllLocalesData_compareLocale() { - //Use LocaleList.matchScore() to compare two locales. - assertTrue(DummyAppLocaleDetailsHelper.compareLocale(Locale.forLanguageTag("en-US"), - Locale.forLanguageTag("en-CA"))); - assertTrue(DummyAppLocaleDetailsHelper.compareLocale(Locale.forLanguageTag("zh-CN"), - Locale.forLanguageTag("zh"))); - assertTrue(DummyAppLocaleDetailsHelper.compareLocale(Locale.forLanguageTag("zh-CN"), - Locale.forLanguageTag("zh-Hans"))); - assertTrue(DummyAppLocaleDetailsHelper.compareLocale(Locale.forLanguageTag("zh-TW"), - Locale.forLanguageTag("zh-Hant"))); - - //Use Locale.equals() to compare two locales. - assertFalse(Locale.forLanguageTag("en-US").equals(Locale.forLanguageTag("en-CA"))); - assertFalse(Locale.forLanguageTag("zh-CN").equals(Locale.forLanguageTag("zh"))); - assertFalse(Locale.forLanguageTag("zh-CN").equals(Locale.forLanguageTag("zh-Hans"))); - assertFalse(Locale.forLanguageTag("zh-TW").equals(Locale.forLanguageTag("zh-Hant"))); - } - - /** - * Sets the initial Locale data - * - * @param appLocale Application locale, it shall be a language tag. - * example: "en" - * - * @param simCountry The ISO-3166-1 alpha-2 country code equivalent for the SIM - * provider's country code. - * example: "us" - * - * @param networkCountry The ISO-3166-1 alpha-2 country code equivalent of the MCC - * (Mobile Country Code) of the current registered operato - * or the cell nearby. - * example: "us" - * - * @param systemLocales System locales, a locale list by a multiple language tags with comma. - * example: "en, uk, jp" - * - * @param packageLocales PackageManager locales, a locale list by a multiple language tags with - * comma. - * example: "en, uk, jp" - * - * @param assetLocales Asset locales, a locale list by a multiple language tags with String - * array. - * example: new String[] {"en", "ne", "ms", "pa"} - */ - private void setupInitialLocales(String appLocale, - String simCountry, - String networkCountry, - String systemLocales, - String packageLocales, - String[] assetLocales) { - mAppLocale = LocaleList.forLanguageTags(appLocale); - // forLanguageTags does not filter space to the input string. If there is any space included - // in string, this will make locale fail to generate. - systemLocales = systemLocales.replaceAll("\\s+", ""); - LocaleList listOfSystemLocales = LocaleList.forLanguageTags(systemLocales); - mSystemLocales = new ArrayList<>(); - for (int i = 0; i < listOfSystemLocales.size(); i++) { - mSystemLocales.add(listOfSystemLocales.get(i)); - } - mAssetLocales = assetLocales; - packageLocales = packageLocales.replaceAll("\\s+", ""); - mPackageLocales = LocaleList.forLanguageTags(packageLocales); - when(mTelephonyManager.getSimCountryIso()).thenReturn(simCountry); - when(mTelephonyManager.getNetworkCountryIso()).thenReturn(networkCountry); - when(mLocaleManager.getApplicationLocales(anyString())).thenReturn(mAppLocale); - } - - public class DummyAppLocaleDetailsHelper - extends AppLocaleDetails.AppLocaleDetailsHelper { - - DummyAppLocaleDetailsHelper(Context context, String packageName) { - super(context, packageName); - } - - @Override - String[] getAssetLocales() { - return mAssetLocales; - } - - @Override - Collection getCurrentSystemLocales() { - return mSystemLocales; - } - - @Override - LocaleList getPackageLocales() { - return mPackageLocales; - } - } -}