Snap for 8391568 from ca7f2dbe4a to tm-release

Change-Id: Ia3ccf74b86b75dc1ede8ec25ec210597b67e36d1
This commit is contained in:
Android Build Coastguard Worker
2022-04-01 01:09:42 +00:00
26 changed files with 905 additions and 1148 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,26 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<path
android:pathData="M12.24 13L9.41 10.17L8 11.59L10.83 14.42C11.61 15.2 12.88 15.2 13.66 14.42L19.42 8.66C19.79 8.28 20 7.77 20 7.24V5.5C20 4.4 19.1 3.5 18 3.5H6C4.9 3.5 4 4.4 4 5.5V12.73C4 16.99 7.22 20.71 11.47 20.99C16.13 21.28 20 17.59 20 13H18C18 16.57 14.87 19.42 11.21 18.95C8.19 18.56 6 15.84 6 12.79V5.5H18V7.24L12.24 13Z"
android:fillColor="#5F6368"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,27 @@
<?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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="24dp"
android:insetRight="24dp">
<shape
android:shape="rectangle">
<solid android:color="?android:attr/colorBackground"/>
<corners
android:radius="@*android:dimen/config_dialogCornerRadius"
/>
</shape>
</inset>

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

@@ -165,6 +165,27 @@
style="@style/BiometricEnrollIntroMessage" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon_shield"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_guarantee"/>
<Space
android:layout_width="16dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/footer_message_6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/BiometricEnrollIntroMessage" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -1009,6 +1009,10 @@
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_5">Your phone can be unlocked when you don\u2019t intend to, like if someone holds it up to your finger.</string>
<!-- Introduction description message shown in fingerprint enrollment introduction screen in setup wizard when asking for parental consent. [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_5">Your child\u2019s phone can be unlocked when they don\u2019t intend to, like if someone holds it up to their finger.</string>
<!-- Introduction description message shown in fingerprint enrollment introduction screen in setup wizard. [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_6">For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your fingerprint may not work.</string>
<!-- Introduction description message shown in fingerprint enrollment introduction screen in setup wizard when asking for parental consent. [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6">For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your child\u2019s fingerprint may not work.</string>
<!-- Introduction detail message shown in fingerprint enrollment introduction to learn more about fingerprint [CHAR LIMIT=NONE]-->
<string name="security_settings_fingerprint_v2_enroll_introduction_message_learn_more"></string>
@@ -1120,8 +1124,10 @@
<string name="security_settings_udfps_enroll_repeat_message">Touch &amp; hold each time the fingerprint icon moves. This helps capture more of your fingerprint.</string>
<!-- Title shown during fingerprint enrollment that instructs the user to enroll their fingertip [CHAR LIMIT=80] -->
<string name="security_settings_udfps_enroll_fingertip_title">Place the tip of your finger on the sensor</string>
<!-- Title shown during fingerprint enrollment that instructs the user to enroll the edges of their finger [CHAR LIMIT=80] -->
<string name="security_settings_udfps_enroll_edge_title">Finally, use the edges of your finger</string>
<!-- Title shown during fingerprint enrollment that instructs the user to enroll the left edge of their finger [CHAR LIMIT=80] -->
<string name="security_settings_udfps_enroll_left_edge_title">Place the left edge of your finger</string>
<!-- Title shown during fingerprint enrollment that instructs the user to enroll the right edge of their finger [CHAR LIMIT=80] -->
<string name="security_settings_udfps_enroll_right_edge_title">Place the right edge of your finger</string>
<!-- Message shown during fingerprint enrollment that instructs the user to enroll the edges of their finger [CHAR LIMIT=160] -->
<string name="security_settings_udfps_enroll_edge_message">Place the side of your fingerprint on the sensor and hold, then switch to the other side</string>
<!-- Message shown in fingerprint enrollment asking users to repeat touching the fingerprint sensor. [CHAR LIMIT=160] -->
@@ -4758,11 +4764,13 @@
<!-- Manage applications, individual application screen, confirmation dialog title. Displays when user selects to "Clear data". -->
<string name="clear_data_dlg_title">Delete app data?</string>
<!-- Manage applications, individual application screen, confirmation dialog message. Displays when user selects to "Clear data". It warns the user of the consequences of clearing the data for an app. -->
<string name="clear_data_dlg_text">This app\u2019s data will be permanently deleted. This includes files, settings, databases, and other app data.</string>
<!-- Manage applications, individual application screen, confirmation dialog button. Displays when user selects to "Clear data". Goes through with the clearing of the data. -->
<string name="clear_data_dlg_text">This app\u2019s data, including files and settings, will be permanently deleted from this device</string>
<!-- Manage applications, individual application screen, confirmation dialog button. -->
<string name="dlg_ok">OK</string>
<!-- Manage applications, individual application screen, confirmation dialog button. Displays when user selects to "Clear data". -->
<string name="dlg_cancel">Cancel</string>
<!-- Manage applications, individual application screen, confirmation dialog button. Displays when user selects to "Clear data". Goes through with the clearing of the data. [CHAR LIMIT=25] -->
<string name="dlg_delete">Delete</string>
<!-- Manage applications, individual application dialog box title. Shown when the user somehow got into a state where it wants to manage some app that isn't found. -->
<string name="app_not_found_dlg_title"></string>
<!-- Manage applications, individual application dialog box message. Shown when the user somehow got into a state where it wants to manage some app that isn't found. -->

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

@@ -16,21 +16,13 @@
package com.android.settings.accessibility;
import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -40,22 +32,17 @@ import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.content.PackageMonitor;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
@@ -63,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -71,9 +57,6 @@ public class AccessibilitySettings extends DashboardFragment {
private static final String TAG = "AccessibilitySettings";
// Index of the first preference in a preference category.
private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
// Preference categories
private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
private static final String CATEGORY_CAPTIONS = "captions_category";
@@ -249,8 +232,7 @@ public class AccessibilitySettings extends DashboardFragment {
* @param serviceEnabled Whether the accessibility service is enabled.
* @return The service summary
*/
@VisibleForTesting
static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
boolean serviceEnabled) {
if (serviceEnabled && info.crashed) {
return context.getText(R.string.accessibility_summary_state_stopped);
@@ -289,8 +271,7 @@ public class AccessibilitySettings extends DashboardFragment {
* @param serviceEnabled Whether the accessibility service is enabled.
* @return The service description
*/
@VisibleForTesting
static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info,
public static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info,
boolean serviceEnabled) {
if (serviceEnabled && info.crashed) {
return context.getText(R.string.accessibility_description_state_stopped);
@@ -506,296 +487,4 @@ public class AccessibilitySettings extends DashboardFragment {
context);
}
};
/**
* This class helps setup RestrictedPreference.
*/
@VisibleForTesting
static class RestrictedPreferenceHelper {
private final Context mContext;
private final DevicePolicyManager mDpm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
RestrictedPreferenceHelper(Context context) {
mContext = context;
mDpm = context.getSystemService(DevicePolicyManager.class);
mPm = context.getPackageManager();
mAppOps = context.getSystemService(AppOpsManager.class);
}
/**
* Creates the list of {@link RestrictedPreference} with the installedServices arguments.
*
* @param installedServices The list of {@link AccessibilityServiceInfo}s of the
* installed accessibility services
* @return The list of {@link RestrictedPreference}
*/
@VisibleForTesting
List<RestrictedPreference> createAccessibilityServicePreferenceList(
List<AccessibilityServiceInfo> installedServices) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedServicesSize = installedServices.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedServicesSize);
for (int i = 0; i < installedServicesSize; ++i) {
final AccessibilityServiceInfo info = installedServices.get(i);
final ResolveInfo resolveInfo = info.getResolveInfo();
final String packageName = resolveInfo.serviceInfo.packageName;
final ComponentName componentName = new ComponentName(packageName,
resolveInfo.serviceInfo.name);
final String key = componentName.flattenToString();
final CharSequence title = resolveInfo.loadLabel(mPm);
final boolean serviceEnabled = enabledServices.contains(componentName);
final CharSequence summary = getServiceSummary(mContext, info, serviceEnabled);
final String fragment = getAccessibilityServiceFragmentTypeName(info);
Drawable icon = resolveInfo.loadIcon(mPm);
if (resolveInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext,
R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, packageName,
resolveInfo.serviceInfo.applicationInfo.uid);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final int imageRes = info.getAnimatedImageRes();
final CharSequence intro = info.loadIntro(mPm);
final CharSequence description = getServiceDescription(mContext, info,
serviceEnabled);
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putServiceExtras(preference, resolveInfo, serviceEnabled);
putSettingsExtras(preference, packageName, settingsClassName);
putTileServiceExtras(preference, packageName, tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
/**
* Create the list of {@link RestrictedPreference} with the installedShortcuts arguments.
*
* @param installedShortcuts The list of {@link AccessibilityShortcutInfo}s of the
* installed accessibility shortcuts
* @return The list of {@link RestrictedPreference}
*/
@VisibleForTesting
List<RestrictedPreference> createAccessibilityActivityPreferenceList(
List<AccessibilityShortcutInfo> installedShortcuts) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedShortcutsSize = installedShortcuts.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedShortcutsSize);
for (int i = 0; i < installedShortcutsSize; ++i) {
final AccessibilityShortcutInfo info = installedShortcuts.get(i);
final ActivityInfo activityInfo = info.getActivityInfo();
final ComponentName componentName = info.getComponentName();
final String key = componentName.flattenToString();
final CharSequence title = activityInfo.loadLabel(mPm);
final String summary = info.loadSummary(mPm);
final String fragment =
LaunchAccessibilityActivityPreferenceFragment.class.getName();
Drawable icon = activityInfo.loadIcon(mPm);
if (activityInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, componentName.getPackageName(),
activityInfo.applicationInfo.uid);
final boolean serviceEnabled = enabledServices.contains(componentName);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final CharSequence intro = info.loadIntro(mPm);
final String description = info.loadDescription(mPm);
final int imageRes = info.getAnimatedImageRes();
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putSettingsExtras(preference, componentName.getPackageName(), settingsClassName);
putTileServiceExtras(preference, componentName.getPackageName(),
tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
private String getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info) {
// Shorten the name to avoid exceeding 100 characters in one line.
final String volumeShortcutToggleAccessibilityServicePreferenceFragment =
VolumeShortcutToggleAccessibilityServicePreferenceFragment.class.getName();
switch (AccessibilityUtil.getAccessibilityServiceFragmentType(info)) {
case AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE:
return volumeShortcutToggleAccessibilityServicePreferenceFragment;
case AccessibilityServiceFragmentType.INVISIBLE_TOGGLE:
return InvisibleToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityServiceFragmentType.TOGGLE:
return ToggleAccessibilityServicePreferenceFragment.class.getName();
default:
// impossible status
throw new AssertionError();
}
}
private RestrictedPreference createRestrictedPreference(String key, CharSequence title,
CharSequence summary, Drawable icon, String fragment, String packageName, int uid) {
final RestrictedPreference preference = new RestrictedPreference(mContext, packageName,
uid);
preference.setKey(key);
preference.setTitle(title);
preference.setSummary(summary);
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
preference.setFragment(fragment);
preference.setIconSize(ICON_SIZE_MEDIUM);
preference.setPersistent(false); // Disable SharedPreferences.
preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
return preference;
}
private void setRestrictedPreferenceEnabled(RestrictedPreference preference,
final List<String> permittedServices, boolean serviceEnabled) {
// permittedServices null means all accessibility services are allowed.
boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName());
boolean appOpsAllowed;
if (serviceAllowed) {
try {
final int mode = mAppOps.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
preference.getUid(), preference.getPackageName());
appOpsAllowed = mode == AppOpsManager.MODE_ALLOWED;
serviceAllowed = appOpsAllowed;
} catch (Exception e) {
// Allow service in case if app ops is not available in testing.
appOpsAllowed = true;
}
} else {
appOpsAllowed = false;
}
if (serviceAllowed || serviceEnabled) {
preference.setEnabled(true);
} else {
// Disable accessibility service that are not permitted.
final EnforcedAdmin admin =
RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
mContext, preference.getPackageName(), UserHandle.myUserId());
if (admin != null) {
preference.setDisabledByAdmin(admin);
} else if (!appOpsAllowed) {
preference.setDisabledByAppOps(true);
} else {
preference.setEnabled(false);
}
}
}
/** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */
private void putBasicExtras(RestrictedPreference preference, String prefKey,
CharSequence title, CharSequence intro, CharSequence summary, int imageRes,
String htmlDescription, ComponentName componentName) {
final Bundle extras = preference.getExtras();
extras.putString(EXTRA_PREFERENCE_KEY, prefKey);
extras.putCharSequence(EXTRA_TITLE, title);
extras.putCharSequence(EXTRA_INTRO, intro);
extras.putCharSequence(EXTRA_SUMMARY, summary);
extras.putParcelable(EXTRA_COMPONENT_NAME, componentName);
extras.putInt(EXTRA_ANIMATED_IMAGE_RES, imageRes);
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
}
/**
* Puts the service extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called by {@link AccessibilityServiceInfo}.</p>
*
* @param preference The preference we are configuring.
* @param resolveInfo The service resolve info.
* @param serviceEnabled Whether the accessibility service is enabled.
*/
private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo,
Boolean serviceEnabled) {
final Bundle extras = preference.getExtras();
extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
}
/**
* Puts the settings extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called when settings UI is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param settingsClassName The component name of an activity that allows the user to modify
* the settings for this accessibility feature.
*/
private void putSettingsExtras(RestrictedPreference preference, String packageName,
String settingsClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(settingsClassName)) {
extras.putString(EXTRA_SETTINGS_TITLE,
mContext.getText(R.string.accessibility_menu_item_settings).toString());
extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
new ComponentName(packageName, settingsClassName).flattenToString());
}
}
/**
* Puts the information about a particular application
* {@link android.service.quicksettings.TileService} into {@link RestrictedPreference}'s
* getExtras().
*
* <p><b>Note:</b> Called when a tooltip of
* {@link android.service.quicksettings.TileService} is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param tileServiceClassName The component name of tileService is associated with this
* accessibility feature.
*/
private void putTileServiceExtras(RestrictedPreference preference, String packageName,
String tileServiceClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(tileServiceClassName)) {
extras.putString(EXTRA_TILE_SERVICE_COMPONENT_NAME,
new ComponentName(packageName, tileServiceClassName).flattenToString());
}
}
}
}

View File

@@ -0,0 +1,337 @@
/*
* 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.accessibility;
import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
import androidx.core.content.ContextCompat;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* This class helps setup RestrictedPreference for accessibility.
*/
public class RestrictedPreferenceHelper {
// Index of the first preference in a preference category.
private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
private final Context mContext;
private final DevicePolicyManager mDpm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
public RestrictedPreferenceHelper(Context context) {
mContext = context;
mDpm = context.getSystemService(DevicePolicyManager.class);
mPm = context.getPackageManager();
mAppOps = context.getSystemService(AppOpsManager.class);
}
/**
* Creates the list of {@link RestrictedPreference} with the installedServices arguments.
*
* @param installedServices The list of {@link AccessibilityServiceInfo}s of the
* installed accessibility services
* @return The list of {@link RestrictedPreference}
*/
public List<RestrictedPreference> createAccessibilityServicePreferenceList(
List<AccessibilityServiceInfo> installedServices) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedServicesSize = installedServices.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedServicesSize);
for (int i = 0; i < installedServicesSize; ++i) {
final AccessibilityServiceInfo info = installedServices.get(i);
final ResolveInfo resolveInfo = info.getResolveInfo();
final String packageName = resolveInfo.serviceInfo.packageName;
final ComponentName componentName = new ComponentName(packageName,
resolveInfo.serviceInfo.name);
final String key = componentName.flattenToString();
final CharSequence title = resolveInfo.loadLabel(mPm);
final boolean serviceEnabled = enabledServices.contains(componentName);
final CharSequence summary = AccessibilitySettings.getServiceSummary(
mContext, info, serviceEnabled);
final String fragment = getAccessibilityServiceFragmentTypeName(info);
Drawable icon = resolveInfo.loadIcon(mPm);
if (resolveInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext,
R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, packageName,
resolveInfo.serviceInfo.applicationInfo.uid);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final int imageRes = info.getAnimatedImageRes();
final CharSequence intro = info.loadIntro(mPm);
final CharSequence description = AccessibilitySettings.getServiceDescription(
mContext, info, serviceEnabled);
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putServiceExtras(preference, resolveInfo, serviceEnabled);
putSettingsExtras(preference, packageName, settingsClassName);
putTileServiceExtras(preference, packageName, tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
/**
* Creates the list of {@link RestrictedPreference} with the installedShortcuts arguments.
*
* @param installedShortcuts The list of {@link AccessibilityShortcutInfo}s of the
* installed accessibility shortcuts
* @return The list of {@link RestrictedPreference}
*/
public List<RestrictedPreference> createAccessibilityActivityPreferenceList(
List<AccessibilityShortcutInfo> installedShortcuts) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedShortcutsSize = installedShortcuts.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedShortcutsSize);
for (int i = 0; i < installedShortcutsSize; ++i) {
final AccessibilityShortcutInfo info = installedShortcuts.get(i);
final ActivityInfo activityInfo = info.getActivityInfo();
final ComponentName componentName = info.getComponentName();
final String key = componentName.flattenToString();
final CharSequence title = activityInfo.loadLabel(mPm);
final String summary = info.loadSummary(mPm);
final String fragment =
LaunchAccessibilityActivityPreferenceFragment.class.getName();
Drawable icon = activityInfo.loadIcon(mPm);
if (activityInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, componentName.getPackageName(),
activityInfo.applicationInfo.uid);
final boolean serviceEnabled = enabledServices.contains(componentName);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final CharSequence intro = info.loadIntro(mPm);
final String description = info.loadDescription(mPm);
final int imageRes = info.getAnimatedImageRes();
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putSettingsExtras(preference, componentName.getPackageName(), settingsClassName);
putTileServiceExtras(preference, componentName.getPackageName(),
tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
private String getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info) {
final int type = AccessibilityUtil.getAccessibilityServiceFragmentType(info);
switch (type) {
case AccessibilityUtil.AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE:
return VolumeShortcutToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityUtil.AccessibilityServiceFragmentType.INVISIBLE_TOGGLE:
return InvisibleToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityUtil.AccessibilityServiceFragmentType.TOGGLE:
return ToggleAccessibilityServicePreferenceFragment.class.getName();
default:
throw new IllegalArgumentException(
"Unsupported accessibility fragment type " + type);
}
}
private RestrictedPreference createRestrictedPreference(String key, CharSequence title,
CharSequence summary, Drawable icon, String fragment, String packageName, int uid) {
final RestrictedPreference preference = new RestrictedPreference(mContext, packageName,
uid);
preference.setKey(key);
preference.setTitle(title);
preference.setSummary(summary);
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
preference.setFragment(fragment);
preference.setIconSize(ICON_SIZE_MEDIUM);
preference.setPersistent(false); // Disable SharedPreferences.
preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
return preference;
}
private void setRestrictedPreferenceEnabled(RestrictedPreference preference,
final List<String> permittedServices, boolean serviceEnabled) {
// permittedServices null means all accessibility services are allowed.
boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName());
boolean appOpsAllowed;
if (serviceAllowed) {
try {
final int mode = mAppOps.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
preference.getUid(), preference.getPackageName());
appOpsAllowed = mode == AppOpsManager.MODE_ALLOWED;
serviceAllowed = appOpsAllowed;
} catch (Exception e) {
// Allow service in case if app ops is not available in testing.
appOpsAllowed = true;
}
} else {
appOpsAllowed = false;
}
if (serviceAllowed || serviceEnabled) {
preference.setEnabled(true);
} else {
// Disable accessibility service that are not permitted.
final RestrictedLockUtils.EnforcedAdmin admin =
RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
mContext, preference.getPackageName(), UserHandle.myUserId());
if (admin != null) {
preference.setDisabledByAdmin(admin);
} else if (!appOpsAllowed) {
preference.setDisabledByAppOps(true);
} else {
preference.setEnabled(false);
}
}
}
/** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */
private void putBasicExtras(RestrictedPreference preference, String prefKey,
CharSequence title, CharSequence intro, CharSequence summary, int imageRes,
String htmlDescription, ComponentName componentName) {
final Bundle extras = preference.getExtras();
extras.putString(AccessibilitySettings.EXTRA_PREFERENCE_KEY, prefKey);
extras.putCharSequence(AccessibilitySettings.EXTRA_TITLE, title);
extras.putCharSequence(AccessibilitySettings.EXTRA_INTRO, intro);
extras.putCharSequence(AccessibilitySettings.EXTRA_SUMMARY, summary);
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, imageRes);
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
}
/**
* Puts the service extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called by {@link AccessibilityServiceInfo}.</p>
*
* @param preference The preference we are configuring.
* @param resolveInfo The service resolve info.
* @param serviceEnabled Whether the accessibility service is enabled.
*/
private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo,
Boolean serviceEnabled) {
final Bundle extras = preference.getExtras();
extras.putParcelable(AccessibilitySettings.EXTRA_RESOLVE_INFO, resolveInfo);
extras.putBoolean(AccessibilitySettings.EXTRA_CHECKED, serviceEnabled);
}
/**
* Puts the settings extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called when settings UI is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param settingsClassName The component name of an activity that allows the user to modify
* the settings for this accessibility feature.
*/
private void putSettingsExtras(RestrictedPreference preference, String packageName,
String settingsClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(settingsClassName)) {
extras.putString(AccessibilitySettings.EXTRA_SETTINGS_TITLE,
mContext.getText(R.string.accessibility_menu_item_settings).toString());
extras.putString(AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME,
new ComponentName(packageName, settingsClassName).flattenToString());
}
}
/**
* Puts the information about a particular application
* {@link android.service.quicksettings.TileService} into {@link RestrictedPreference}'s
* getExtras().
*
* <p><b>Note:</b> Called when a tooltip of
* {@link android.service.quicksettings.TileService} is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param tileServiceClassName The component name of tileService is associated with this
* accessibility feature.
*/
private void putTileServiceExtras(RestrictedPreference preference, String packageName,
String tileServiceClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(tileServiceClassName)) {
extras.putString(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME,
new ComponentName(packageName, tileServiceClassName).flattenToString());
}
}
}

View File

@@ -491,7 +491,8 @@ public class AppStorageSettings extends AppInfoWithHeader
return new AlertDialog.Builder(getActivity())
.setTitle(getActivity().getText(R.string.clear_data_dlg_title))
.setMessage(getActivity().getText(R.string.clear_data_dlg_text))
.setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
.setPositiveButton(R.string.dlg_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Clear user data here
initiateClearUserData();

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

@@ -17,14 +17,20 @@
package com.android.settings.applications.appinfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppLocaleUtil;
import com.android.settings.localepicker.AppLocalePickerActivity;
import java.util.List;
@@ -59,6 +65,23 @@ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBa
return AppLocaleDetails.getSummary(mContext, mParent.getAppEntry().info.packageName);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), mPreferenceKey)) {
return false;
}
if (mParent != null) {
Intent intent = new Intent(mContext, AppLocalePickerActivity.class);
intent.setData(Uri.parse("package:" + mParent.getAppEntry().info.packageName));
mContext.startActivity(intent);
return true;
} else {
Log.d(TAG, "mParent is null");
return false;
}
}
@VisibleForTesting
boolean canDisplayLocaleUi() {
return AppLocaleUtil

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

@@ -380,7 +380,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
break;
case STAGE_LEFT_EDGE:
setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
setHeaderText(R.string.security_settings_udfps_enroll_left_edge_title);
if (!mHaveShownUdfpsLeftEdgeLottie && mIllustrationLottie != null) {
mHaveShownUdfpsLeftEdgeLottie = true;
setDescriptionText("");
@@ -399,7 +399,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
}
break;
case STAGE_RIGHT_EDGE:
setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
setHeaderText(R.string.security_settings_udfps_enroll_right_edge_title);
if (!mHaveShownUdfpsRightEdgeLottie && mIllustrationLottie != null) {
mHaveShownUdfpsRightEdgeLottie = true;
setDescriptionText("");

View File

@@ -91,10 +91,12 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
final TextView footerMessage3 = findViewById(R.id.footer_message_3);
final TextView footerMessage4 = findViewById(R.id.footer_message_4);
final TextView footerMessage5 = findViewById(R.id.footer_message_5);
final TextView footerMessage6 = findViewById(R.id.footer_message_6);
footerMessage2.setText(getFooterMessage2());
footerMessage3.setText(getFooterMessage3());
footerMessage4.setText(getFooterMessage4());
footerMessage5.setText(getFooterMessage5());
footerMessage6.setText(getFooterMessage6());
final TextView footerTitle1 = findViewById(R.id.footer_title_1);
final TextView footerTitle2 = findViewById(R.id.footer_title_2);
@@ -163,6 +165,11 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
}
@StringRes
protected int getFooterMessage6() {
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_6;
}
@Override
protected boolean isDisabledByAdmin() {
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(

View File

@@ -44,7 +44,8 @@ public class FingerprintEnrollParentalConsent extends FingerprintEnrollIntroduct
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_2,
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_3,
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_4,
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_5
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_5,
R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6
};
@Override
@@ -116,6 +117,11 @@ public class FingerprintEnrollParentalConsent extends FingerprintEnrollIntroduct
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_5;
}
@StringRes
protected int getFooterMessage6() {
return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6;
}
@Override
protected int getHeaderResDefault() {
return R.string.security_settings_fingerprint_enroll_consent_introduction_title;

View File

@@ -27,6 +27,7 @@ import android.util.Log;
import androidx.annotation.CallSuper;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
@@ -43,7 +44,6 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;

View File

@@ -0,0 +1,118 @@
/*
* 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.app.LocaleManager;
import android.os.Bundle;
import android.os.LocaleList;
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();
private String mPackageName;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackageName = getIntent().getData().getSchemeSpecificPart();
if (TextUtils.isEmpty(mPackageName)) {
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(mPackageName);
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 localeInfo) {
if (localeInfo == null || localeInfo.getLocale() == null || localeInfo.isSystemLocale()) {
setAppDefaultLocale("");
} else {
setAppDefaultLocale(localeInfo.getLocale().getLanguage());
}
finish();
}
/** Sets the app's locale to the supplied language tag */
private void setAppDefaultLocale(String languageTag) {
LocaleManager localeManager = getSystemService(LocaleManager.class);
if (localeManager == null) {
Log.w(TAG, "LocaleManager is null, cannot set default app locale");
return;
}
localeManager.setApplicationLocales(mPackageName, LocaleList.forLanguageTags(languageTag));
}
}

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

@@ -26,6 +26,8 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import com.android.settings.R;
/** Fragment to show a progress dialog. */
public class ProgressDialogFragment extends DialogFragment {
private static final String ARG_TITLE = "title";
@@ -83,6 +85,7 @@ public class ProgressDialogFragment extends DialogFragment {
@SuppressWarnings("deprecation") // ProgressDialog is deprecated but is intended UX for now
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.getWindow().setBackgroundDrawableResource(R.drawable.sim_progress_dialog_rounded_bg);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setMessage(getArguments().getString(ARG_TITLE));

View File

@@ -217,19 +217,17 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
Log.i(TAG, "User confirmed to enable the subscription.");
showProgressDialog(
getString(
R.string.sim_action_switch_sub_dialog_progress,
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this)));
if (mIsEsimOperation) {
showProgressDialog(
getString(
R.string.sim_action_switch_sub_dialog_progress,
SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this)));
mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
UiccSlotUtil.INVALID_PORT_ID,
removedSubInfo);
return;
}
showProgressDialog(
getString(R.string.sim_action_enabling_sim_without_carrier_name));
mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
removedSubInfo);
break;

View File

@@ -29,13 +29,9 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AlignmentSpan;
import android.text.style.BulletSpan;
import android.text.style.ClickableSpan;
import android.text.style.RelativeSizeSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -58,7 +54,7 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment {
private PaymentBackend mPaymentBackend;
private List<PaymentAppInfo> mAppInfos;
private Preference mFooterPreference;
private FooterPreference mFooterPreference;
@Override
public int getMetricsCategory() {
@@ -249,31 +245,12 @@ public class DefaultPaymentSettings extends DefaultAppPickerFragment {
}
private void setupFooterPreference() {
final String textNfcDefaultPaymentFooter = getResources().getString(
R.string.nfc_default_payment_footer);
final String textMoreDetails = getResources().getString(R.string.nfc_more_details);
final SpannableString spannableString = new SpannableString(
textNfcDefaultPaymentFooter + System.lineSeparator()
+ System.lineSeparator() + textMoreDetails);
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Intent howItWorksIntent = new Intent(getActivity(), HowItWorks.class);
startActivity(howItWorksIntent);
}
};
if (textNfcDefaultPaymentFooter != null && textMoreDetails != null) {
spannableString.setSpan(clickableSpan, textNfcDefaultPaymentFooter.length() + 1,
textNfcDefaultPaymentFooter.length() + textMoreDetails.length() + 2,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mFooterPreference = new FooterPreference(getContext());
mFooterPreference.setLayoutResource(R.layout.preference_footer);
mFooterPreference.setTitle(spannableString);
mFooterPreference.setSelectable(false);
mFooterPreference.setTitle(getResources().getString(R.string.nfc_default_payment_footer));
mFooterPreference.setIcon(R.drawable.ic_info_outline_24dp);
mFooterPreference.setLearnMoreAction(v -> {
final Intent howItWorksIntent = new Intent(getActivity(), HowItWorks.class);
getContext().startActivity(howItWorksIntent);
});
}
}

View File

@@ -87,8 +87,6 @@ public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
private static final int ON = 1;
private static final int OFF = 0;
private static final String EMPTY_STRING = "";
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
@@ -246,37 +244,6 @@ public class AccessibilitySettingsTest {
assertThat(description).isEqualTo(DEFAULT_DESCRIPTION);
}
@Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
final AccessibilitySettings.RestrictedPreferenceHelper helper =
new AccessibilitySettings.RestrictedPreferenceHelper(mContext);
final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
singletonList(mServiceInfo));
final List<RestrictedPreference> preferenceList =
helper.createAccessibilityServicePreferenceList(infoList);
RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.getKey()).isEqualTo(key);
}
@Test
public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
final AccessibilitySettings.RestrictedPreferenceHelper helper =
new AccessibilitySettings.RestrictedPreferenceHelper(mContext);
setMockAccessibilityShortcutInfo(mShortcutInfo);
final List<AccessibilityShortcutInfo> infoList = new ArrayList<>(
singletonList(mShortcutInfo));
final List<RestrictedPreference> preferenceList =
helper.createAccessibilityActivityPreferenceList(infoList);
RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.getKey()).isEqualTo(key);
}
@Test
@Config(shadows = {ShadowFragment.class, ShadowUserManager.class})
public void onCreate_haveRegisterToSpecificUrisAndActions() {

View File

@@ -0,0 +1,133 @@
/*
* 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.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static java.util.Collections.singletonList;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.RestrictedPreference;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** Test for {@link RestrictedPreferenceHelper}. */
@RunWith(RobolectricTestRunner.class)
public class RestrictedPreferenceHelperTest {
private static final String PACKAGE_NAME = "com.android.test";
private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
private static final String DEFAULT_LABEL = "default label";
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
PACKAGE_NAME, CLASS_NAME);
@Mock
private AccessibilityShortcutInfo mShortcutInfo;
private final RestrictedPreferenceHelper mHelper = new RestrictedPreferenceHelper(mContext);
@Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
final List<AccessibilityServiceInfo> infoList = new ArrayList<>(
singletonList(mServiceInfo));
final List<RestrictedPreference> preferenceList =
mHelper.createAccessibilityServicePreferenceList(infoList);
final RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.getKey()).isEqualTo(key);
}
@Test
public void createAccessibilityActivityPreferenceList_hasOneInfo_containsSameKey() {
final String key = COMPONENT_NAME.flattenToString();
setMockAccessibilityShortcutInfo(mShortcutInfo);
final List<AccessibilityShortcutInfo> infoList = new ArrayList<>(
singletonList(mShortcutInfo));
final List<RestrictedPreference> preferenceList =
mHelper.createAccessibilityActivityPreferenceList(infoList);
final RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.getKey()).isEqualTo(key);
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
String className) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
final ServiceInfo serviceInfo = new ServiceInfo();
applicationInfo.packageName = packageName;
serviceInfo.packageName = packageName;
serviceInfo.name = className;
serviceInfo.applicationInfo = applicationInfo;
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = serviceInfo;
try {
final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo,
mContext);
info.setComponentName(new ComponentName(packageName, className));
return info;
} catch (XmlPullParserException | IOException e) {
// Do nothing
}
return null;
}
private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
activityInfo.applicationInfo = new ApplicationInfo();
when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
}
}

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;
}
}
}