Merge "[Panlingual] Revamp the panlingual UI in Settings." into tm-dev

This commit is contained in:
Tom Hsu
2022-03-31 08:32:52 +00:00
committed by Android (Google) Code Review
9 changed files with 273 additions and 757 deletions

View File

@@ -864,7 +864,7 @@
</activity>
<activity
android:name=".Settings$AppLocalePickerActivity"
android:name=".localepicker.AppLocalePickerActivity"
android:label="@string/app_locale_picker_title"
android:exported="true" >
<intent-filter>
@@ -872,8 +872,6 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
</activity>
<activity

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/app_locale_detail_container"
android:orientation="vertical">
<FrameLayout
android:id="@+id/app_locale_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/app_locale_picker_with_region"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@@ -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">
<com.android.settingslib.widget.LayoutPreference
android:key="app_locale_description"
android:layout="@layout/app_locale_details_description"
@@ -26,19 +27,4 @@
settings:allowDividerBelow="true"
settings:searchable="false"/>
<PreferenceCategory
android:key="category_key_suggested_languages"
android:title="@string/suggested_app_locales_title" >
<com.android.settingslib.widget.RadioButtonPreference
android:key="system_default_locale"
android:title="@string/preference_of_system_locale_title"
android:order="-10000"/>
</PreferenceCategory>
<PreferenceCategory
android:key="category_key_all_languages"
android:title="@string/all_supported_app_locales_title" />
</PreferenceScreen>

View File

@@ -111,8 +111,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 */ }

View File

@@ -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<Locale> 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<Locale> mProcessedSuggestedLocales = new ArrayList<>();
private Collection<Locale> mProcessedSupportedLocales = new ArrayList<>();
private Collection<Locale> 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<Locale> getSuggestedLocales() {
return mProcessedSuggestedLocales;
}
/** Gets supported locales in the app. */
public Collection<Locale> 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<Locale> 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<Locale> getAppSupportedLocales() {
Collection<Locale> 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<Locale> getCurrentSystemLocales() {
LocaleList localeList = Resources.getSystem().getConfiguration().getLocales();
Collection<Locale> 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;
}
}
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> 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<Locale> suggestedLocales = helper.getSuggestedLocales();
assertFalse(hasDuplicatedResult(suggestedLocales));
}
private boolean hasDuplicatedResult(Collection<Locale> locales) {
Set<Locale> 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<Locale> getCurrentSystemLocales() {
return mSystemLocales;
}
@Override
LocaleList getPackageLocales() {
return mPackageLocales;
}
}
}