diff --git a/src/com/android/settings/applications/AppLocaleUtil.java b/src/com/android/settings/applications/AppLocaleUtil.java index 79406f05224..70d284da214 100644 --- a/src/com/android/settings/applications/AppLocaleUtil.java +++ b/src/com/android/settings/applications/AppLocaleUtil.java @@ -17,48 +17,55 @@ package com.android.settings.applications; import android.annotation.NonNull; -import android.app.ActivityManager; import android.app.LocaleConfig; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.LocaleList; +import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import com.android.settings.R; import java.util.List; -/** This class provides methods that help dealing with per app locale. */ +/** + * This class provides methods that help dealing with per app locale. + */ public class AppLocaleUtil { private static final String TAG = AppLocaleUtil.class.getSimpleName(); public static final Intent LAUNCHER_ENTRY_INTENT = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER); + @VisibleForTesting + static LocaleConfig sLocaleConfig; + /** * Decides the UI display of per app locale. */ public static boolean canDisplayLocaleUi( @NonNull Context context, - @NonNull String packageName, + @NonNull ApplicationInfo app, @NonNull List infos) { - boolean isDisallowedPackage = isDisallowedPackage(context, packageName); - boolean hasLauncherEntry = hasLauncherEntry(packageName, infos); - boolean isSignedWithPlatformKey = isSignedWithPlatformKey(context, packageName); + boolean isDisallowedPackage = isDisallowedPackage(context, app.packageName); + boolean hasLauncherEntry = hasLauncherEntry(app.packageName, infos); + boolean isSignedWithPlatformKey = app.isSignedWithPlatformKey(); boolean canDisplay = !isDisallowedPackage && !isSignedWithPlatformKey && hasLauncherEntry - && isAppLocaleSupported(context, packageName); + && isAppLocaleSupported(context, app.packageName); - Log.i(TAG, "Can display preference - [" + packageName + "] :" + Log.i(TAG, "Can display preference - [" + app.packageName + "] :" + " isDisallowedPackage : " + isDisallowedPackage + " / isSignedWithPlatformKey : " + isSignedWithPlatformKey + " / hasLauncherEntry : " + hasLauncherEntry - + " / canDisplay : " + canDisplay); + + " / canDisplay : " + canDisplay + " / 1.1"); return canDisplay; } @@ -66,30 +73,13 @@ public class AppLocaleUtil { final String[] disallowedPackages = context.getResources().getStringArray( R.array.config_disallowed_app_localeChange_packages); for (String disallowedPackage : disallowedPackages) { - if (packageName.equals(disallowedPackage)) { + if (TextUtils.equals(packageName, 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(); - } - private static boolean hasLauncherEntry(String packageName, List infos) { return infos.stream() .anyMatch(info -> info.activityInfo.packageName.equals(packageName)); @@ -99,7 +89,13 @@ public class AppLocaleUtil { * Check the function of per app language is supported by current application. */ public static boolean isAppLocaleSupported(Context context, String packageName) { - LocaleList localeList = getPackageLocales(context, packageName); + LocaleList localeList; + if (sLocaleConfig != null) { + localeList = getPackageLocales(sLocaleConfig); + } else { + localeList = getPackageLocales(context, packageName); + } + if (localeList != null) { return localeList.size() > 0; } @@ -118,9 +114,8 @@ public class AppLocaleUtil { public static String[] getAssetLocales(Context context, String packageName) { try { PackageManager packageManager = context.getPackageManager(); - String[] locales = packageManager.getResourcesForApplication( - packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL) - .applicationInfo).getAssets().getNonSystemLocales(); + String[] locales = packageManager.getResourcesForApplication(packageName) + .getAssets().getNonSystemLocales(); if (locales == null) { Log.i(TAG, "[" + packageName + "] locales are null."); } @@ -137,6 +132,14 @@ public class AppLocaleUtil { return new String[0]; } + @VisibleForTesting + static LocaleList getPackageLocales(LocaleConfig localeConfig) { + if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { + return localeConfig.getSupportedLocales(); + } + return null; + } + /** * Get locales from LocaleConfig. */ @@ -144,9 +147,7 @@ public class AppLocaleUtil { try { LocaleConfig localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0)); - if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { - return localeConfig.getSupportedLocales(); - } + return getPackageLocales(localeConfig); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } diff --git a/src/com/android/settings/applications/AppStateLocaleBridge.java b/src/com/android/settings/applications/AppStateLocaleBridge.java index 8262ce7f8cc..2fea004778d 100644 --- a/src/com/android/settings/applications/AppStateLocaleBridge.java +++ b/src/com/android/settings/applications/AppStateLocaleBridge.java @@ -55,8 +55,7 @@ public class AppStateLocaleBridge extends AppStateBaseBridge { AppInfoByProfiles appInfoByProfiles = getAppInfo(UserHandle.getUserId(uid)); app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(appInfoByProfiles.mContextAsUser, - app.info.packageName, - appInfoByProfiles.mListInfos) ? Boolean.TRUE : Boolean.FALSE; + app.info, appInfoByProfiles.mListInfos) ? Boolean.TRUE : Boolean.FALSE; } @Override @@ -67,8 +66,7 @@ public class AppStateLocaleBridge extends AppStateBaseBridge { AppInfoByProfiles appInfoByProfiles = getAppInfo(UserHandle.getUserId(app.info.uid)); app.extraInfo = AppLocaleUtil.canDisplayLocaleUi(appInfoByProfiles.mContextAsUser, - app.info.packageName, - appInfoByProfiles.mListInfos) ? Boolean.TRUE : Boolean.FALSE; + app.info, appInfoByProfiles.mListInfos) ? Boolean.TRUE : Boolean.FALSE; } } diff --git a/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java b/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java index 30fe1c4363a..d58607fa66f 100644 --- a/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java @@ -84,6 +84,6 @@ public class AppLocalePreferenceController extends AppInfoPreferenceControllerBa @VisibleForTesting boolean canDisplayLocaleUi() { return AppLocaleUtil - .canDisplayLocaleUi(mContext, mParent.getAppEntry().info.packageName, mListInfos); + .canDisplayLocaleUi(mContext, mParent.getAppEntry().info, mListInfos); } } diff --git a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt index 68308d72ece..b9607b03339 100644 --- a/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt +++ b/src/com/android/settings/spa/app/appinfo/AppLocalePreference.kt @@ -69,7 +69,7 @@ private class AppLocalePresenter( ResolveInfoFlags.of(PackageManager.GET_META_DATA.toLong()), app.userId, ) - AppLocaleUtil.canDisplayLocaleUi(context, app.packageName, resolveInfos) + AppLocaleUtil.canDisplayLocaleUi(context, app, resolveInfos) } val summaryFlow = flow { emit(getSummary()) } diff --git a/src/com/android/settings/spa/system/AppLanguagesListModel.kt b/src/com/android/settings/spa/system/AppLanguagesListModel.kt index 15a0b859243..50b23d39e19 100644 --- a/src/com/android/settings/spa/system/AppLanguagesListModel.kt +++ b/src/com/android/settings/spa/system/AppLanguagesListModel.kt @@ -58,7 +58,7 @@ class AppLanguagesListModel(private val context: Context) : AppListModel AppLanguagesRecord(app, AppLocaleUtil.canDisplayLocaleUi(context, - app.packageName, resolveInfos)) + app, resolveInfos)) } } diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt index 688ced10e81..60c4f79ad9e 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt @@ -77,7 +77,8 @@ class AppLocalePreferenceTest { .strictness(Strictness.LENIENT) .startMocking() whenever(context.packageManager).thenReturn(packageManager) - whenever(AppLocaleUtil.canDisplayLocaleUi(any(), eq(PACKAGE_NAME), any())).thenReturn(true) + whenever(AppLocaleUtil.canDisplayLocaleUi(any(), ArgumentMatchers.eq(APP), any())) + .thenReturn(true) whenever(AppLocaleDetails.getSummary(any(), ArgumentMatchers.eq(APP))).thenReturn(SUMMARY) } @@ -88,7 +89,8 @@ class AppLocalePreferenceTest { @Test fun whenCanNotDisplayLocalUi_notDisplayed() { - whenever(AppLocaleUtil.canDisplayLocaleUi(any(), eq(PACKAGE_NAME), any())).thenReturn(false) + whenever(AppLocaleUtil.canDisplayLocaleUi(any(), ArgumentMatchers.eq(APP), any())) + .thenReturn(false) setContent() diff --git a/tests/unit/src/com/android/settings/applications/AppLocaleUtilTest.java b/tests/unit/src/com/android/settings/applications/AppLocaleUtilTest.java index 8350bc70b2e..8a7f5045f85 100644 --- a/tests/unit/src/com/android/settings/applications/AppLocaleUtilTest.java +++ b/tests/unit/src/com/android/settings/applications/AppLocaleUtilTest.java @@ -17,27 +17,29 @@ package com.android.settings.applications; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.LocaleConfig; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.os.LocaleList; +import android.util.FeatureFlagUtils; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -53,14 +55,15 @@ public class AppLocaleUtilTest { @Mock private ActivityManager mActivityManager; @Mock - private ApplicationInfo mApplicationInfo; - @Mock private Resources mResources; + @Mock + private LocaleConfig mLocaleConfig; private Context mContext; private String mDisallowedPackage = "com.disallowed.package"; private String mAllowedPackage = "com.allowed.package"; private List mListResolveInfo; + private ApplicationInfo mAppInfo; @Before public void setUp() { @@ -68,47 +71,72 @@ public class AppLocaleUtilTest { mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); + + FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED, + true); setDisallowedPackageName(mDisallowedPackage); + mAppInfo = new ApplicationInfo(); + mLocaleConfig = mock(LocaleConfig.class); + } + + @After + public void tearDown() { + AppLocaleUtil.sLocaleConfig = null; } @Test - @Ignore("b/231904717") - public void canDisplayLocaleUi_showUI() throws PackageManager.NameNotFoundException { - setApplicationInfo(/*no platform key*/ false); + public void canDisplayLocaleUi_showUI() { + when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS); + when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US")); + AppLocaleUtil.sLocaleConfig = mLocaleConfig; setActivityInfo(mAllowedPackage); + mAppInfo.packageName = mAllowedPackage; - assertTrue(AppLocaleUtil.canDisplayLocaleUi(mContext, mAllowedPackage, mListResolveInfo)); + assertTrue(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo)); } @Test - @Ignore("b/231904717") - public void canDisplayLocaleUi_notShowUI_hasPlatformKey() - throws PackageManager.NameNotFoundException { - setApplicationInfo(/*has platform key*/ true); + public void canDisplayLocaleUi_notShowUI_hasPlatformKey() { setActivityInfo(mAllowedPackage); + mAppInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY; + mAppInfo.packageName = mAllowedPackage; - assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAllowedPackage, mListResolveInfo)); + assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo)); } @Test - @Ignore("b/231904717") - public void canDisplayLocaleUi_notShowUI_noLauncherEntry() - throws PackageManager.NameNotFoundException { - setApplicationInfo(/*no platform key*/false); + public void canDisplayLocaleUi_notShowUI_noLauncherEntry() { setActivityInfo(""); - assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAllowedPackage, mListResolveInfo)); + assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo)); } @Test - @Ignore("b/231904717") - public void canDisplayLocaleUi_notShowUI_matchDisallowedPackageList() - throws PackageManager.NameNotFoundException { - setApplicationInfo(/*no platform key*/false); + public void canDisplayLocaleUi_notShowUI_matchDisallowedPackageList() { setActivityInfo(""); + mAppInfo.packageName = mDisallowedPackage; assertFalse(AppLocaleUtil - .canDisplayLocaleUi(mContext, mDisallowedPackage, mListResolveInfo)); + .canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo)); + } + + @Test + public void getPackageLocales_getLocales_success() { + when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS); + when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US")); + + LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig); + + assertFalse(result.isEmpty()); + } + + @Test + public void getPackageLocales_getLocales_failed() { + when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_PARSING_FAILED); + + LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig); + + assertNull(result); } private void setDisallowedPackageName(String packageName) { @@ -116,20 +144,6 @@ public class AppLocaleUtilTest { when(mResources.getStringArray(anyInt())).thenReturn(new String[]{packageName}); } - private void setApplicationInfo(boolean signedWithPlatformKey) - throws PackageManager.NameNotFoundException { - ApplicationInfo applicationInfo = new ApplicationInfo(); - if (signedWithPlatformKey) { - applicationInfo.privateFlags = applicationInfo.privateFlags - | ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY; - } - - PackageInfo packageInfo = new PackageInfo(); - packageInfo.applicationInfo = applicationInfo; - when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn( - packageInfo); - } - private void setActivityInfo(String packageName) { ResolveInfo resolveInfo = mock(ResolveInfo.class); ActivityInfo activityInfo = mock(ActivityInfo.class);