Loading res/values/config.xml +10 −0 Original line number Diff line number Diff line Loading @@ -573,4 +573,14 @@ <!-- Whether to give option to add restricted profiles --> <bool name="config_offer_restricted_profiles">false</bool> <!-- An array of packages for which Applications whose per-app locale cannot be changed. --> <string-array name="config_disallowed_app_localeChange_packages" translatable="false"> <!-- <item>com.example.package.first</item> <item>com.example.package.second</item> <item>...</item> --> </string-array> </resources> src/com/android/settings/applications/AppLocaleUtil.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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.applications; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; import com.android.settings.R; import com.android.settingslib.applications.ApplicationsState.AppEntry; /** This class provides methods that help dealing with per app locale. */ public class AppLocaleUtil { private static final String TAG = AppLocaleUtil.class.getSimpleName(); /** * Decides the UI display of per app locale. */ public static boolean canDisplayLocaleUi(Context context, AppEntry app) { return !isDisallowedPackage(context, app.info.packageName) && !isSignedWithPlatformKey(context, app.info.packageName) && app.hasLauncherEntry; } private static boolean isDisallowedPackage(Context context, String packageName) { final String[] disallowedPackages = context.getResources().getStringArray( R.array.config_disallowed_app_localeChange_packages); for (String disallowedPackage : disallowedPackages) { if (packageName.equals(disallowedPackage)) { return true; } } return false; } private static boolean isSignedWithPlatformKey(Context context, String packageName) { PackageInfo packageInfo = null; PackageManager packageManager = context.getPackageManager(); ActivityManager activityManager = context.getSystemService(ActivityManager.class); try { packageInfo = packageManager.getPackageInfoAsUser( packageName, /* flags= */ 0, activityManager.getCurrentUser()); } catch (PackageManager.NameNotFoundException ex) { Log.e(TAG, "package not found: " + packageName); } if (packageInfo == null) { return false; } return packageInfo.applicationInfo.isSignedWithPlatformKey(); } } src/com/android/settings/applications/AppStateLocaleBridge.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * 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.applications; import android.content.Context; import android.util.Log; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; import java.util.List; /** * Creates a application filter to restrict UI display of applications. * This is to avoid users from changing the per apps locale * Also provides app filters that can use the info. */ public class AppStateLocaleBridge extends AppStateBaseBridge { private static final String TAG = AppStateLocaleBridge.class.getSimpleName(); private final Context mContext; public AppStateLocaleBridge(Context context, ApplicationsState appState, Callback callback) { super(appState, callback); mContext = context; } @Override protected void updateExtraInfo(AppEntry app, String packageName, int uid) { app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(mContext, app) ? Boolean.TRUE : Boolean.FALSE; } @Override protected void loadAllExtraInfo() { final List<AppEntry> allApps = mAppSession.getAllApps(); for (int i = 0; i < allApps.size(); i++) { AppEntry app = allApps.get(i); app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(mContext, app) ? Boolean.TRUE : Boolean.FALSE; } } /** For the Settings which shows category of per app's locale. */ public static final AppFilter FILTER_APPS_LOCALE = new AppFilter() { @Override public void init() { } @Override public boolean filterApp(AppEntry entry) { if (entry.extraInfo == null) { Log.d(TAG, "No extra info."); return false; } return (Boolean) entry.extraInfo; } }; } src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java +10 −3 Original line number Diff line number Diff line Loading @@ -20,20 +20,23 @@ import android.content.Context; import android.util.FeatureFlagUtils; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.applications.AppLocaleUtil; /** * A controller to update current locale information of application. */ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBase { private static final String TAG = AppLocalePreferenceController.class.getSimpleName(); public AppLocalePreferenceController(Context context, String key) { super(context, key); } @Override public int getAvailabilityStatus() { return FeatureFlagUtils .isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; boolean isFeatureOn = FeatureFlagUtils .isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION); return isFeatureOn && canDisplayLocaleUi() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override Loading @@ -45,4 +48,8 @@ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBa public CharSequence getSummary() { return AppLocaleDetails.getSummary(mContext, mParent.getAppEntry().info.packageName); } boolean canDisplayLocaleUi() { return AppLocaleUtil.canDisplayLocaleUi(mContext, mParent.getAppEntry()); } } src/com/android/settings/applications/manageapplications/AppFilterRegistry.java +15 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import androidx.annotation.IntDef; import com.android.settings.R; import com.android.settings.applications.AppStateAlarmsAndRemindersBridge; import com.android.settings.applications.AppStateInstallAppsBridge; import com.android.settings.applications.AppStateLocaleBridge; import com.android.settings.applications.AppStateManageExternalStorageBridge; import com.android.settings.applications.AppStateMediaManagementAppsBridge; import com.android.settings.applications.AppStateNotificationBridge; Loading Loading @@ -54,6 +55,7 @@ public class AppFilterRegistry { FILTER_APPS_BLOCKED, FILTER_ALARMS_AND_REMINDERS, FILTER_APPS_MEDIA_MANAGEMENT, FILTER_APPS_LOCALE, }) @interface FilterType { } Loading @@ -79,14 +81,15 @@ public class AppFilterRegistry { public static final int FILTER_MANAGE_EXTERNAL_STORAGE = 17; public static final int FILTER_ALARMS_AND_REMINDERS = 18; public static final int FILTER_APPS_MEDIA_MANAGEMENT = 19; // Next id: 20. If you add an entry here, length of mFilters should be updated public static final int FILTER_APPS_LOCALE = 20; // Next id: 21. If you add an entry here, length of mFilters should be updated private static AppFilterRegistry sRegistry; private final AppFilterItem[] mFilters; private AppFilterRegistry() { mFilters = new AppFilterItem[20]; mFilters = new AppFilterItem[21]; // High power allowlist, on mFilters[FILTER_APPS_POWER_ALLOWLIST] = new AppFilterItem( Loading Loading @@ -203,8 +206,16 @@ public class AppFilterRegistry { AppStateMediaManagementAppsBridge.FILTER_MEDIA_MANAGEMENT_APPS, FILTER_APPS_MEDIA_MANAGEMENT, R.string.media_management_apps_title); // Apps that can configurate appication's locale. mFilters[FILTER_APPS_LOCALE] = new AppFilterItem( AppStateLocaleBridge.FILTER_APPS_LOCALE, FILTER_APPS_LOCALE, R.string.app_locale_picker_title); } public static AppFilterRegistry getInstance() { if (sRegistry == null) { sRegistry = new AppFilterRegistry(); Loading Loading @@ -235,6 +246,8 @@ public class AppFilterRegistry { return FILTER_ALARMS_AND_REMINDERS; case ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS: return FILTER_APPS_MEDIA_MANAGEMENT; case ManageApplications.LIST_TYPE_APPS_LOCALE: return FILTER_APPS_LOCALE; default: return FILTER_APPS_ALL; } Loading Loading
res/values/config.xml +10 −0 Original line number Diff line number Diff line Loading @@ -573,4 +573,14 @@ <!-- Whether to give option to add restricted profiles --> <bool name="config_offer_restricted_profiles">false</bool> <!-- An array of packages for which Applications whose per-app locale cannot be changed. --> <string-array name="config_disallowed_app_localeChange_packages" translatable="false"> <!-- <item>com.example.package.first</item> <item>com.example.package.second</item> <item>...</item> --> </string-array> </resources>
src/com/android/settings/applications/AppLocaleUtil.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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.applications; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; import com.android.settings.R; import com.android.settingslib.applications.ApplicationsState.AppEntry; /** This class provides methods that help dealing with per app locale. */ public class AppLocaleUtil { private static final String TAG = AppLocaleUtil.class.getSimpleName(); /** * Decides the UI display of per app locale. */ public static boolean canDisplayLocaleUi(Context context, AppEntry app) { return !isDisallowedPackage(context, app.info.packageName) && !isSignedWithPlatformKey(context, app.info.packageName) && app.hasLauncherEntry; } private static boolean isDisallowedPackage(Context context, String packageName) { final String[] disallowedPackages = context.getResources().getStringArray( R.array.config_disallowed_app_localeChange_packages); for (String disallowedPackage : disallowedPackages) { if (packageName.equals(disallowedPackage)) { return true; } } return false; } private static boolean isSignedWithPlatformKey(Context context, String packageName) { PackageInfo packageInfo = null; PackageManager packageManager = context.getPackageManager(); ActivityManager activityManager = context.getSystemService(ActivityManager.class); try { packageInfo = packageManager.getPackageInfoAsUser( packageName, /* flags= */ 0, activityManager.getCurrentUser()); } catch (PackageManager.NameNotFoundException ex) { Log.e(TAG, "package not found: " + packageName); } if (packageInfo == null) { return false; } return packageInfo.applicationInfo.isSignedWithPlatformKey(); } }
src/com/android/settings/applications/AppStateLocaleBridge.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * 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.applications; import android.content.Context; import android.util.Log; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; import java.util.List; /** * Creates a application filter to restrict UI display of applications. * This is to avoid users from changing the per apps locale * Also provides app filters that can use the info. */ public class AppStateLocaleBridge extends AppStateBaseBridge { private static final String TAG = AppStateLocaleBridge.class.getSimpleName(); private final Context mContext; public AppStateLocaleBridge(Context context, ApplicationsState appState, Callback callback) { super(appState, callback); mContext = context; } @Override protected void updateExtraInfo(AppEntry app, String packageName, int uid) { app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(mContext, app) ? Boolean.TRUE : Boolean.FALSE; } @Override protected void loadAllExtraInfo() { final List<AppEntry> allApps = mAppSession.getAllApps(); for (int i = 0; i < allApps.size(); i++) { AppEntry app = allApps.get(i); app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(mContext, app) ? Boolean.TRUE : Boolean.FALSE; } } /** For the Settings which shows category of per app's locale. */ public static final AppFilter FILTER_APPS_LOCALE = new AppFilter() { @Override public void init() { } @Override public boolean filterApp(AppEntry entry) { if (entry.extraInfo == null) { Log.d(TAG, "No extra info."); return false; } return (Boolean) entry.extraInfo; } }; }
src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java +10 −3 Original line number Diff line number Diff line Loading @@ -20,20 +20,23 @@ import android.content.Context; import android.util.FeatureFlagUtils; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.applications.AppLocaleUtil; /** * A controller to update current locale information of application. */ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBase { private static final String TAG = AppLocalePreferenceController.class.getSimpleName(); public AppLocalePreferenceController(Context context, String key) { super(context, key); } @Override public int getAvailabilityStatus() { return FeatureFlagUtils .isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; boolean isFeatureOn = FeatureFlagUtils .isEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LANGUAGE_SELECTION); return isFeatureOn && canDisplayLocaleUi() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } @Override Loading @@ -45,4 +48,8 @@ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBa public CharSequence getSummary() { return AppLocaleDetails.getSummary(mContext, mParent.getAppEntry().info.packageName); } boolean canDisplayLocaleUi() { return AppLocaleUtil.canDisplayLocaleUi(mContext, mParent.getAppEntry()); } }
src/com/android/settings/applications/manageapplications/AppFilterRegistry.java +15 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import androidx.annotation.IntDef; import com.android.settings.R; import com.android.settings.applications.AppStateAlarmsAndRemindersBridge; import com.android.settings.applications.AppStateInstallAppsBridge; import com.android.settings.applications.AppStateLocaleBridge; import com.android.settings.applications.AppStateManageExternalStorageBridge; import com.android.settings.applications.AppStateMediaManagementAppsBridge; import com.android.settings.applications.AppStateNotificationBridge; Loading Loading @@ -54,6 +55,7 @@ public class AppFilterRegistry { FILTER_APPS_BLOCKED, FILTER_ALARMS_AND_REMINDERS, FILTER_APPS_MEDIA_MANAGEMENT, FILTER_APPS_LOCALE, }) @interface FilterType { } Loading @@ -79,14 +81,15 @@ public class AppFilterRegistry { public static final int FILTER_MANAGE_EXTERNAL_STORAGE = 17; public static final int FILTER_ALARMS_AND_REMINDERS = 18; public static final int FILTER_APPS_MEDIA_MANAGEMENT = 19; // Next id: 20. If you add an entry here, length of mFilters should be updated public static final int FILTER_APPS_LOCALE = 20; // Next id: 21. If you add an entry here, length of mFilters should be updated private static AppFilterRegistry sRegistry; private final AppFilterItem[] mFilters; private AppFilterRegistry() { mFilters = new AppFilterItem[20]; mFilters = new AppFilterItem[21]; // High power allowlist, on mFilters[FILTER_APPS_POWER_ALLOWLIST] = new AppFilterItem( Loading Loading @@ -203,8 +206,16 @@ public class AppFilterRegistry { AppStateMediaManagementAppsBridge.FILTER_MEDIA_MANAGEMENT_APPS, FILTER_APPS_MEDIA_MANAGEMENT, R.string.media_management_apps_title); // Apps that can configurate appication's locale. mFilters[FILTER_APPS_LOCALE] = new AppFilterItem( AppStateLocaleBridge.FILTER_APPS_LOCALE, FILTER_APPS_LOCALE, R.string.app_locale_picker_title); } public static AppFilterRegistry getInstance() { if (sRegistry == null) { sRegistry = new AppFilterRegistry(); Loading Loading @@ -235,6 +246,8 @@ public class AppFilterRegistry { return FILTER_ALARMS_AND_REMINDERS; case ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS: return FILTER_APPS_MEDIA_MANAGEMENT; case ManageApplications.LIST_TYPE_APPS_LOCALE: return FILTER_APPS_LOCALE; default: return FILTER_APPS_ALL; } Loading