1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 import android.app.LocaleConfig; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.os.LocaleList; 26 import android.text.TextUtils; 27 import android.util.FeatureFlagUtils; 28 import android.util.Log; 29 30 import androidx.annotation.NonNull; 31 import androidx.annotation.VisibleForTesting; 32 33 import com.android.settings.R; 34 35 import java.util.List; 36 37 /** 38 * This class provides methods that help dealing with per app locale. 39 */ 40 public class AppLocaleUtil { 41 private static final String TAG = AppLocaleUtil.class.getSimpleName(); 42 43 public static final Intent LAUNCHER_ENTRY_INTENT = 44 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER); 45 46 @VisibleForTesting 47 static LocaleConfig sLocaleConfig; 48 49 /** 50 * Decides the UI display of per app locale. 51 */ canDisplayLocaleUi( @onNull Context context, @NonNull ApplicationInfo app, @NonNull List<ResolveInfo> infos)52 public static boolean canDisplayLocaleUi( 53 @NonNull Context context, 54 @NonNull ApplicationInfo app, 55 @NonNull List<ResolveInfo> infos) { 56 boolean isDisallowedPackage = isDisallowedPackage(context, app.packageName); 57 boolean hasLauncherEntry = hasLauncherEntry(app.packageName, infos); 58 boolean isSignedWithPlatformKey = app.isSignedWithPlatformKey(); 59 boolean canDisplay = !isDisallowedPackage 60 && !isSignedWithPlatformKey 61 && hasLauncherEntry 62 && isAppLocaleSupported(context, app.packageName); 63 64 Log.i(TAG, "Can display preference - [" + app.packageName + "] :" 65 + " isDisallowedPackage : " + isDisallowedPackage 66 + " / isSignedWithPlatformKey : " + isSignedWithPlatformKey 67 + " / hasLauncherEntry : " + hasLauncherEntry 68 + " / canDisplay : " + canDisplay + " / 1.1"); 69 return canDisplay; 70 } 71 isDisallowedPackage(Context context, String packageName)72 private static boolean isDisallowedPackage(Context context, String packageName) { 73 final String[] disallowedPackages = context.getResources().getStringArray( 74 R.array.config_disallowed_app_localeChange_packages); 75 for (String disallowedPackage : disallowedPackages) { 76 if (TextUtils.equals(packageName, disallowedPackage)) { 77 return true; 78 } 79 } 80 return false; 81 } 82 hasLauncherEntry(String packageName, List<ResolveInfo> infos)83 private static boolean hasLauncherEntry(String packageName, List<ResolveInfo> infos) { 84 return infos.stream() 85 .anyMatch(info -> info.activityInfo.packageName.equals(packageName)); 86 } 87 88 /** 89 * Check the function of per app language is supported by current application. 90 */ isAppLocaleSupported(Context context, String packageName)91 public static boolean isAppLocaleSupported(Context context, String packageName) { 92 LocaleList localeList; 93 if (sLocaleConfig != null) { 94 localeList = getPackageLocales(sLocaleConfig); 95 } else { 96 localeList = getPackageLocales(context, packageName); 97 } 98 99 if (localeList != null) { 100 return localeList.size() > 0; 101 } 102 103 if (FeatureFlagUtils.isEnabled( 104 context, FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED)) { 105 return false; 106 } 107 108 return getAssetLocales(context, packageName).length > 0; 109 } 110 111 /** 112 * Get locales fron AssetManager. 113 */ getAssetLocales(Context context, String packageName)114 public static String[] getAssetLocales(Context context, String packageName) { 115 try { 116 PackageManager packageManager = context.getPackageManager(); 117 String[] locales = packageManager.getResourcesForApplication(packageName) 118 .getAssets().getNonSystemLocales(); 119 if (locales == null) { 120 Log.i(TAG, "[" + packageName + "] locales are null."); 121 } 122 if (locales.length <= 0) { 123 Log.i(TAG, "[" + packageName + "] locales length is 0."); 124 return new String[0]; 125 } 126 String locale = locales[0]; 127 Log.i(TAG, "First asset locale - [" + packageName + "] " + locale); 128 return locales; 129 } catch (PackageManager.NameNotFoundException e) { 130 Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); 131 } 132 return new String[0]; 133 } 134 135 @VisibleForTesting getPackageLocales(LocaleConfig localeConfig)136 static LocaleList getPackageLocales(LocaleConfig localeConfig) { 137 if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { 138 return localeConfig.getSupportedLocales(); 139 } 140 return null; 141 } 142 143 /** 144 * Get locales from LocaleConfig. 145 */ getPackageLocales(Context context, String packageName)146 public static LocaleList getPackageLocales(Context context, String packageName) { 147 try { 148 LocaleConfig localeConfig = 149 new LocaleConfig(context.createPackageContext(packageName, 0)); 150 return getPackageLocales(localeConfig); 151 } catch (PackageManager.NameNotFoundException e) { 152 Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); 153 } 154 return null; 155 } 156 } 157