• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.app;
18 
19 import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;
20 
21 import android.app.LocaleConfig;
22 import android.content.Context;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.InstallSourceInfo;
25 import android.content.pm.PackageManager;
26 import android.os.LocaleList;
27 import android.util.Log;
28 
29 import java.util.HashSet;
30 import java.util.Locale;
31 import java.util.stream.Collectors;
32 
33 class AppLocaleStore {
34     private static final String TAG = AppLocaleStore.class.getSimpleName();
35 
getAppSupportedLocales( Context context, String packageName)36     public static AppLocaleResult getAppSupportedLocales(
37             Context context, String packageName) {
38         LocaleConfig localeConfig = null;
39         AppLocaleResult.LocaleStatus localeStatus = LocaleStatus.UNKNOWN_FAILURE;
40         HashSet<Locale> appSupportedLocales = new HashSet<>();
41         HashSet<Locale> assetLocale = getAssetLocales(context, packageName);
42 
43         try {
44             localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0));
45         } catch (PackageManager.NameNotFoundException e) {
46             Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
47         }
48 
49         if (localeConfig != null) {
50             if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
51                 LocaleList packageLocaleList = localeConfig.getSupportedLocales();
52                 boolean shouldFilterNotMatchingLocale = !hasInstallerInfo(context, packageName) &&
53                         isSystemApp(context, packageName);
54 
55                 Log.d(TAG, "filterNonMatchingLocale. " +
56                         ", shouldFilterNotMatchingLocale: " + shouldFilterNotMatchingLocale +
57                         ", assetLocale size: " + assetLocale.size() +
58                         ", packageLocaleList size: " + packageLocaleList.size());
59 
60                 for (int i = 0; i < packageLocaleList.size(); i++) {
61                     appSupportedLocales.add(packageLocaleList.get(i));
62                 }
63                 if (shouldFilterNotMatchingLocale) {
64                     appSupportedLocales = filterNotMatchingLocale(appSupportedLocales, assetLocale);
65                 }
66 
67                 if (appSupportedLocales.size() > 0) {
68                     localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
69                 } else {
70                     localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE_IN_APP;
71                 }
72             } else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) {
73                 if (assetLocale.size() > 0) {
74                     localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
75                     appSupportedLocales = assetLocale;
76                 } else {
77                     localeStatus = LocaleStatus.ASSET_LOCALE_IS_EMPTY;
78                 }
79             }
80         }
81         Log.d(TAG, "getAppSupportedLocales(). package: " + packageName
82                 + ", status: " + localeStatus
83                 + ", appSupportedLocales:" + appSupportedLocales.size());
84         return new AppLocaleResult(localeStatus, appSupportedLocales);
85     }
86 
getAssetLocales(Context context, String packageName)87     private static HashSet<Locale> getAssetLocales(Context context, String packageName) {
88         HashSet<Locale> result = new HashSet<>();
89         try {
90             PackageManager packageManager = context.getPackageManager();
91             String[] locales = packageManager.getResourcesForApplication(
92                     packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL)
93                             .applicationInfo).getAssets().getNonSystemLocales();
94             if (locales == null) {
95                 Log.i(TAG, "[" + packageName + "] locales are null.");
96             } else if (locales.length <= 0) {
97                 Log.i(TAG, "[" + packageName + "] locales length is 0.");
98             } else {
99                 for (String language : locales) {
100                     result.add(Locale.forLanguageTag(language));
101                 }
102             }
103         } catch (PackageManager.NameNotFoundException e) {
104             Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
105         }
106         return result;
107     }
108 
filterNotMatchingLocale( HashSet<Locale> appSupportedLocales, HashSet<Locale> assetLocale)109     private static HashSet<Locale> filterNotMatchingLocale(
110             HashSet<Locale> appSupportedLocales, HashSet<Locale> assetLocale) {
111         return appSupportedLocales.stream()
112                 .filter(locale -> matchLanguageInSet(locale, assetLocale))
113                 .collect(Collectors.toCollection(HashSet::new));
114     }
115 
matchLanguageInSet(Locale locale, HashSet<Locale> localesSet)116     private static boolean matchLanguageInSet(Locale locale, HashSet<Locale> localesSet) {
117         if (localesSet.contains(locale)) {
118             return true;
119         }
120         for (Locale l: localesSet) {
121             if(LocaleList.matchesLanguageAndScript(l, locale)) {
122                 return true;
123             }
124         }
125         return false;
126     }
127 
hasInstallerInfo(Context context, String packageName)128     private static boolean hasInstallerInfo(Context context, String packageName) {
129         InstallSourceInfo installSourceInfo;
130         try {
131             installSourceInfo = context.getPackageManager().getInstallSourceInfo(packageName);
132             return installSourceInfo != null;
133         } catch (PackageManager.NameNotFoundException e) {
134             Log.w(TAG, "Installer info not found for: " + packageName);
135         }
136         return false;
137     }
138 
isSystemApp(Context context, String packageName)139     private static boolean isSystemApp(Context context, String packageName) {
140         ApplicationInfo applicationInfo;
141         try {
142             applicationInfo = context.getPackageManager()
143                     .getApplicationInfoAsUser(packageName, /* flags= */ 0, context.getUserId());
144             return applicationInfo.isSystemApp();
145         } catch (PackageManager.NameNotFoundException e) {
146             Log.w(TAG, "Application info not found for: " + packageName);
147         }
148         return false;
149     }
150 
151     static class AppLocaleResult {
152         enum LocaleStatus {
153             UNKNOWN_FAILURE,
154             NO_SUPPORTED_LANGUAGE_IN_APP,
155             ASSET_LOCALE_IS_EMPTY,
156             GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
157             GET_SUPPORTED_LANGUAGE_FROM_ASSET,
158         }
159 
160         LocaleStatus mLocaleStatus;
161         HashSet<Locale> mAppSupportedLocales;
162 
AppLocaleResult(LocaleStatus localeStatus, HashSet<Locale> appSupportedLocales)163         public AppLocaleResult(LocaleStatus localeStatus, HashSet<Locale> appSupportedLocales) {
164             this.mLocaleStatus = localeStatus;
165             this.mAppSupportedLocales = appSupportedLocales;
166         }
167     }
168 }
169