[Panlingual] Improve performance of displaying app list in Settings.

 - Use existed ApplicationInfo to get needed info.
 - Add unittest to AppLocaleUtil.java

Bug: b/238054165
Bug: b/231904717
Test: Maunal test passed
Test: atest passed
Change-Id: I23f354c366b45d4b684184b3366aeab913bcd3c8
diff --git a/src/com/android/settings/applications/AppLocaleUtil.java b/src/com/android/settings/applications/AppLocaleUtil.java
index 79406f0..70d284d 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<ResolveInfo> 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 @@
         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<ResolveInfo> infos) {
         return infos.stream()
                 .anyMatch(info -> info.activityInfo.packageName.equals(packageName));
@@ -99,7 +89,13 @@
      * 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 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 @@
         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 @@
         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 8262ce7..2fea004 100644
--- a/src/com/android/settings/applications/AppStateLocaleBridge.java
+++ b/src/com/android/settings/applications/AppStateLocaleBridge.java
@@ -55,8 +55,7 @@
         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 @@
             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 30fe1c4..d58607f 100644
--- a/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppLocalePreferenceController.java
@@ -84,6 +84,6 @@
     @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 68308d7..b9607b0 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 @@
             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 15a0b85..50b23d3 100644
--- a/src/com/android/settings/spa/system/AppLanguagesListModel.kt
+++ b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
@@ -58,7 +58,7 @@
             appList.map { app ->
                 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 688ced1..60c4f79 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 @@
             .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 @@
 
     @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 8350bc7..8a7f504 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 @@
     @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<ResolveInfo> mListResolveInfo;
+    private ApplicationInfo mAppInfo;
 
     @Before
     public void setUp() {
@@ -68,47 +71,72 @@
         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 @@
         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);