[Panlingual] Implement entries of Panlingual in settings UI part.

- Path 1 : System -> Language & Input -> App Languages -> per app
 language
 - path 2 : System -> Apps -> App Info -> per app language
 Video: https://screencast.googleplex.com/cast/NjUyNTUzMDY0MTc5MzAyNHxjNTg2MzMwYS0yOA

Bug: 206854105
Test: local test pass
Test: atest pass

Change-Id: Ic06fa4f0c6ec68680eb828032b6e3f479b412dc4
This commit is contained in:
tom hsu
2021-10-28 14:50:11 +08:00
parent f731c74fb4
commit 58244b1b74
14 changed files with 871 additions and 6 deletions

View File

@@ -111,6 +111,7 @@
<uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
<uses-permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<application
android:name=".SettingsApplication"

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal" >
<TextView
android:id="@id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:textAlignment="center"/>
</LinearLayout>

View File

@@ -461,7 +461,7 @@
<string name="next_label">Next</string>
<!-- LocalePicker -->
<!-- Title for the locale picker activity -->
<!-- Title for the locale picker activity. [CHAR LIMIT=30]-->
<string name="language_picker_title">Languages</string>
<!-- Menu item in the locale menu. Will remove the selected locales. [CHAR LIMIT=30] -->
@@ -470,6 +470,29 @@
<!-- "Button" that opens a language picker. The selected language gets added to the language list. [CHAR LIMIT=30] -->
<string name="add_a_language">Add a language</string>
<!-- Title of preference for the locale picker activity. [CHAR LIMIT=30]-->
<string name="app_locale_preference_title">Language</string>
<!-- Title for the locale picker category. [CHAR LIMIT=50]-->
<string name="locale_picker_category_title">Preferred Language</string>
<!-- Title for the Apps' locale menu entry [CHAR LIMIT=50]-->
<string name="app_locales_picker_menu_title">App Languages</string>
<!-- Summary for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_summary">Set the language for each app</string>
<!-- Title for the App's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_title">App Language</string>
<!-- Category for the suggested app's locales. [CHAR LIMIT=50]-->
<string name="suggested_app_locales_title">Suggested languages</string>
<!-- Category for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="all_supported_app_locales_title">All languages</string>
<!-- Description for the app without any supported languages. [CHAR LIMIT=NONE]-->
<string name="no_multiple_language_supported">The app is set to <xliff:g id="default_language" example="English (United States)">%1$s</xliff:g> by default and doesn\u2019t support multiple languages.</string>
<!-- The title of the confirmation dialog shown when the user selects one / several languages and tries to remove them [CHAR LIMIT=60] -->
<plurals name="dlg_remove_locales_title">
<item quantity="one">Remove selected language?</item>

View File

@@ -88,6 +88,12 @@
android:title="@string/power_usage_summary_title"
android:summary="@string/summary_placeholder" />
<Preference
android:key="app_language_setting"
android:title="@string/app_locale_preference_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppLocalePreferenceController" />
<Preference
android:key="preferred_settings"
android:title="@string/launch_by_default"

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
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"
android:selectable="false"
android:visibility="gone"
settings:allowDividerBelow="true"
settings:searchable="false"/>
<PreferenceCategory
android:key="category_key_suggested_languages"
android:title="@string/suggested_app_locales_title" />
<PreferenceCategory
android:key="category_key_all_languages"
android:title="@string/all_supported_app_locales_title" />
</PreferenceScreen>

View File

@@ -19,12 +19,28 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/language_settings">
<PreferenceCategory
android:key="languages_category"
android:title="@string/locale_picker_category_title">
<Preference
android:key="phone_language"
android:title="@string/phone_language"
android:icon="@drawable/ic_translate_24dp"
android:fragment="com.android.settings.localepicker.LocaleListEditor" />
<Preference
android:key="phone_language"
android:title="@string/phone_language"
android:icon="@drawable/ic_translate_24dp"
android:fragment="com.android.settings.localepicker.LocaleListEditor" />
<Preference
android:key="apps_language"
android:title="@string/app_locales_picker_menu_title"
android:summary="@string/app_locale_picker_summary"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
settings:controller="com.android.settings.applications.appinfo.ManageAppLocalePreferenceController">
<extra
android:name="classname"
android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
</Preference>
</PreferenceCategory>
<PreferenceCategory
android:key="keyboards_category"

View File

@@ -205,6 +205,10 @@ public class AppInfoDashboardFragment extends DashboardFragment
use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList(
writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles,
alarmsAndReminders));
final AppLocalePreferenceController appLocale =
use(AppLocalePreferenceController.class);
appLocale.setParentFragment(this);
}
@Override

View File

@@ -0,0 +1,314 @@
/*
* 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 com.android.settings.widget.EntityHeaderController.ActionType;
import android.app.Activity;
import android.app.LocaleManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
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.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.Locale;
/**
* A fragment to show the current app locale info and help the user to select the expected locale.
*/
public class AppLocaleDetails extends AppInfoBase implements RadioButtonPreference.OnClickListener {
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";
private boolean mCreated = false;
private AppLocaleDetailsHelper mAppLocaleDetailsHelper;
private PreferenceGroup mGroupOfSuggestedLocales;
private PreferenceGroup mGroupOfSupportedLocales;
private LayoutPreference mPrefOfDescription;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_locale_details);
mAppLocaleDetailsHelper = new AppLocaleDetailsHelper(getContext(), mPackageName);
mGroupOfSuggestedLocales =
getPreferenceScreen().findPreference(CATEGORY_KEY_SUGGESTED_LANGUAGES);
mGroupOfSupportedLocales =
getPreferenceScreen().findPreference(CATEGORY_KEY_ALL_LANGUAGES);
mPrefOfDescription = getPreferenceScreen().findPreference(KEY_APP_DESCRIPTION);
}
// Override here so we don't have an empty screen
@Override
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) {
return inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
}
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onResume() {
// Update Locales first, before refresh ui.
mAppLocaleDetailsHelper.handleAllLocalesData();
super.onResume();
}
@Override
protected boolean refreshUi() {
if (mAppLocaleDetailsHelper.getSupportedLocales().isEmpty()) {
Log.d(TAG, "No supported language.");
mGroupOfSuggestedLocales.setVisible(false);
mGroupOfSupportedLocales.setVisible(false);
mPrefOfDescription.setVisible(true);
TextView description = (TextView) mPrefOfDescription.findViewById(R.id.description);
Locale locale = mAppLocaleDetailsHelper.getCurrentSystemLocales().get(0);
description.setText(getContext().getString(R.string.no_multiple_language_supported,
locale.getDisplayName(locale)));
return true;
}
mGroupOfSuggestedLocales.removeAll();
mGroupOfSupportedLocales.removeAll();
Locale appLocale = AppLocaleDetailsHelper.getAppDefaultLocale(getContext(), mPackageName);
setLanguagesPreference(mGroupOfSuggestedLocales,
mAppLocaleDetailsHelper.getSuggestedLocales(), appLocale);
setLanguagesPreference(mGroupOfSupportedLocales,
mAppLocaleDetailsHelper.getSupportedLocales(), appLocale);
return true;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.APPS_LOCALE_LIST;
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
return null;
}
@Override
public void onRadioButtonClicked(RadioButtonPreference pref) {
mAppLocaleDetailsHelper.setAppDefaultLocale(pref.getKey());
refreshUi();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mCreated) {
Log.w(TAG, "onActivityCreated: ignoring duplicate call");
return;
}
mCreated = true;
if (mPackageInfo == null) {
return;
}
// Creates a head icon button of app on this page.
final Activity activity = getActivity();
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))
.setPackageName(mPackageName)
.setUid(mPackageInfo.applicationInfo.uid)
.setHasAppInfoLink(true)
.setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
}
/**
* 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);
return appLocale == null ? "" : appLocale.getDisplayName(appLocale);
}
private void setLanguagesPreference(PreferenceGroup group,
Collection<Locale> locales, Locale appLocale) {
if (locales == null) {
return;
}
for (Locale locale : locales) {
RadioButtonPreference pref = new RadioButtonPreference(getContext());
pref.setTitle(locale.getDisplayName(locale));
pref.setKey(locale.toLanguageTag());
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> mSuggestedLocales = new ArrayList<>();;
private Collection<Locale> mSupportedLocales = new ArrayList<>();;
AppLocaleDetailsHelper(Context context, String packageName) {
mContext = context;
mPackageName = packageName;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mLocaleManager = context.getSystemService(LocaleManager.class);
}
/** 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 mSuggestedLocales;
}
/** Gets supported locales in the app. */
public Collection<Locale> getSupportedLocales() {
return mSupportedLocales;
}
@VisibleForTesting
void handleSuggestedLocales() {
LocaleList currentSystemLocales = getCurrentSystemLocales();
Locale simLocale = mTelephonyManager.getSimLocale();
Locale appLocale = getAppDefaultLocale(mContext, mPackageName);
// 1st locale in suggested languages group.
if (appLocale != null) {
mSuggestedLocales.add(appLocale);
}
// 2nd locale in suggested languages group.
if (simLocale != null && !simLocale.equals(appLocale)) {
mSuggestedLocales.add(simLocale);
}
// Other locales in suggested languages group.
for (int i = 0; i < currentSystemLocales.size(); i++) {
Locale locale = currentSystemLocales.get(i);
if (!locale.equals(appLocale) && !locale.equals(simLocale)) {
mSuggestedLocales.add(locale);
}
}
}
@VisibleForTesting
void handleSupportedLocales() {
//TODO Waiting for PackageManager api
String[] languages = getAssetSystemLocales();
for (String language : languages) {
mSupportedLocales.add(Locale.forLanguageTag(language));
}
if (mSuggestedLocales != null || !mSuggestedLocales.isEmpty()) {
mSupportedLocales.removeAll(mSuggestedLocales);
}
}
private void clearLocalesData() {
mSuggestedLocales.clear();
mSupportedLocales.clear();
}
/** Gets per app's default locale */
public static Locale getAppDefaultLocale(Context context, String packageName) {
LocaleManager localeManager = context.getSystemService(LocaleManager.class);
LocaleList localeList = (localeManager == null)
? new LocaleList() : localeManager.getApplicationLocales(packageName);
return localeList.isEmpty() ? null : localeList.get(0);
}
/** 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
LocaleList getCurrentSystemLocales() {
return Resources.getSystem().getConfiguration().getLocales();
}
@VisibleForTesting
String[] getAssetSystemLocales() {
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 : " + e);
}
return new String[0];
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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 android.content.Context;
import android.util.FeatureFlagUtils;
import com.android.settings.SettingsPreferenceFragment;
/**
* A controller to update current locale information of application.
*/
public class AppLocalePreferenceController extends AppInfoPreferenceControllerBase {
public AppLocalePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppLocaleDetails.class;
}
@Override
public CharSequence getSummary() {
return AppLocaleDetails.getSummary(mContext, mParent.getAppEntry().info.packageName);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 android.content.Context;
import android.util.FeatureFlagUtils;
import com.android.settings.core.BasePreferenceController;
/**
* A controller to update current locale information of application
* and a entry to launch {@link ManageApplications}.
* TODO(209775925) After feature release, this class may be removed.
*/
public class ManageAppLocalePreferenceController extends BasePreferenceController {
public ManageAppLocalePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -108,6 +108,7 @@ import com.android.settings.applications.AppStorageSettings;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.AppLocaleDetails;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.ManageExternalStorageDetails;
@@ -231,6 +232,7 @@ public class ManageApplications extends InstrumentedFragment
public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11;
public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12;
public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 13;
public static final int LIST_TYPE_APPS_LOCAL = 14;
// List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
@@ -318,6 +320,8 @@ public class ManageApplications extends InstrumentedFragment
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
} else if (className.equals(AppLocaleDetails.class.getName())) {
mListType = LIST_TYPE_APPS_LOCAL;
} else {
mListType = LIST_TYPE_MAIN;
}
@@ -500,6 +504,8 @@ public class ManageApplications extends InstrumentedFragment
return SettingsEnums.ALARMS_AND_REMINDERS;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return SettingsEnums.MEDIA_MANAGEMENT_APPS;
case LIST_TYPE_APPS_LOCAL:
return SettingsEnums.APPS_LOCALE_LIST;
default:
return SettingsEnums.PAGE_UNKNOWN;
}
@@ -623,6 +629,10 @@ public class ManageApplications extends InstrumentedFragment
startAppInfoFragment(MediaManagementAppsDetails.class,
R.string.media_management_apps_title);
break;
case LIST_TYPE_APPS_LOCAL:
startAppInfoFragment(AppLocaleDetails.class,
R.string.app_locale_picker_title);
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
// app. Maybe when they load the list of apps that contains managed profile apps.
@@ -899,6 +909,8 @@ public class ManageApplications extends InstrumentedFragment
screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
screenTitle = R.string.app_notifications_title;
} else if (className.equals(AppLocaleDetails.class.getName())) {
screenTitle = R.string.app_locales_picker_menu_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.all_apps;
@@ -1521,6 +1533,10 @@ public class ManageApplications extends InstrumentedFragment
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
holder.setSummary(MediaManagementAppsDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_APPS_LOCAL:
holder.setSummary(AppLocaleDetails
.getSummary(mContext, entry.info.packageName));
break;
default:
holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
break;

View File

@@ -0,0 +1,198 @@
/*
* 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.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.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Locale;
@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 LocaleList mSystemLocales;
private Locale mSimLocale;
private LocaleList mAppLocale;
private String[] mAssetLocales;
@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("en",
"uk",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
}
@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_1stLocaleOfSuggestedLocaleListIsAppLocale() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mAppLocale.get(0)));
}
@Test
@UiThreadTest
public void handleAllLocalesData_2ndLocaleOfSuggestedLocaleListIsSimLocale() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 1);
assertTrue(locale.equals(mSimLocale));
}
@Test
@UiThreadTest
public void handleAllLocalesData_withoutAppLocale_1stLocaleOfSuggestedLocaleListIsSimLocal() {
setupInitialLocales("",
"uk",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mSimLocale));
}
@Test
@UiThreadTest
public void handleAllLocalesData_noAppAndSimLocale_1stLocaleIsFirstOneInSystemLocales() {
setupInitialLocales("",
"",
"en, uk, jp, ne",
new String[]{"en", "ne", "ms", "pa"});
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
Locale locale = Iterables.get(helper.getSuggestedLocales(), 0);
assertTrue(locale.equals(mSystemLocales.get(0)));
}
@Test
@UiThreadTest
public void handleAllLocalesData_supportLocaleListIsNotEmpty() {
DummyAppLocaleDetailsHelper helper =
new DummyAppLocaleDetailsHelper(mContext, APP_PACKAGE_NAME);
helper.handleAllLocalesData();
assertFalse(helper.getSupportedLocales().isEmpty());
}
/**
* Sets the initial Locale data
*
* @param appLocale Application locale, it shall be a language tag.
* example: "en"
* @param simLocale SIM carrier locale, it shall be a language tag.
* example: "en"
* @param systemLocales System 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 simLocale,
String systemLocales,
String[] assetLocales) {
mAppLocale = LocaleList.forLanguageTags(appLocale);
mSimLocale = Locale.forLanguageTag(simLocale);
mSystemLocales = LocaleList.forLanguageTags(systemLocales);
mAssetLocales = assetLocales;
when(mTelephonyManager.getSimLocale()).thenReturn(simLocale.isEmpty() ? null : mSimLocale);
when(mLocaleManager.getApplicationLocales(anyString())).thenReturn(mAppLocale);
}
private class DummyAppLocaleDetailsHelper
extends AppLocaleDetails.AppLocaleDetailsHelper {
DummyAppLocaleDetailsHelper(Context context, String packageName) {
super(context, packageName);
}
@Override
String[] getAssetSystemLocales() {
return mAssetLocales;
}
@Override
LocaleList getCurrentSystemLocales() {
return mSystemLocales;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class AppLocalePreferenceControllerTest {
private Context mContext;
private AppLocalePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = spy(new AppLocalePreferenceController(mContext, "test_key"));
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, true);
}
@Test
public void getAvailabilityStatus_featureFlagOff_shouldReturnUnavailable() {
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, false);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_featureFlagOn_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.util.FeatureFlagUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ManageAppLocalePreferenceControllerTest {
private Context mContext;
private ManageAppLocalePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = spy(new ManageAppLocalePreferenceController(mContext, "a key"));
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, true);
}
@Test
public void getAvailabilityStatus_featureFlagOff_shouldReturnUnavailable() {
FeatureFlagUtils
.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION, false);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_featureFlagOn_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
}
}