blob: a450a05dd315ae4e9c1db32590e0dca3f85eb87a [file] [log] [blame]
/*
* 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.internal.app;
import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;
import android.app.LocaleConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
import android.os.LocaleList;
import android.util.Log;
import java.util.HashSet;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* A class used to access an application's supporting locales.
*/
public class AppLocaleStore {
private static final String TAG = AppLocaleStore.class.getSimpleName();
public static AppLocaleResult getAppSupportedLocales(
Context context, String packageName) {
LocaleConfig localeConfig = null;
AppLocaleResult.LocaleStatus localeStatus = LocaleStatus.UNKNOWN_FAILURE;
HashSet<Locale> appSupportedLocales = new HashSet<>();
HashSet<Locale> assetLocale = getAssetLocales(context, packageName);
try {
localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
}
if (localeConfig != null) {
if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
LocaleList packageLocaleList = localeConfig.getSupportedLocales();
boolean shouldFilterNotMatchingLocale = !hasInstallerInfo(context, packageName) &&
isSystemApp(context, packageName);
Log.d(TAG, "filterNonMatchingLocale. " +
", shouldFilterNotMatchingLocale: " + shouldFilterNotMatchingLocale +
", assetLocale size: " + assetLocale.size() +
", packageLocaleList size: " + packageLocaleList.size());
for (int i = 0; i < packageLocaleList.size(); i++) {
appSupportedLocales.add(packageLocaleList.get(i));
}
if (shouldFilterNotMatchingLocale) {
appSupportedLocales = filterNotMatchingLocale(appSupportedLocales, assetLocale);
}
if (appSupportedLocales.size() > 0) {
localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
} else {
localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE_IN_APP;
}
} else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) {
if (assetLocale.size() > 0) {
localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
appSupportedLocales = assetLocale;
} else {
localeStatus = LocaleStatus.ASSET_LOCALE_IS_EMPTY;
}
}
}
Log.d(TAG, "getAppSupportedLocales(). package: " + packageName
+ ", status: " + localeStatus
+ ", appSupportedLocales:" + appSupportedLocales.size());
return new AppLocaleResult(localeStatus, appSupportedLocales);
}
private static HashSet<Locale> getAssetLocales(Context context, String packageName) {
HashSet<Locale> result = new HashSet<>();
try {
PackageManager packageManager = context.getPackageManager();
String[] locales = packageManager.getResourcesForApplication(
packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL)
.applicationInfo).getAssets().getNonSystemLocales();
if (locales == null) {
Log.i(TAG, "[" + packageName + "] locales are null.");
} else if (locales.length <= 0) {
Log.i(TAG, "[" + packageName + "] locales length is 0.");
} else {
for (String language : locales) {
result.add(Locale.forLanguageTag(language));
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
}
return result;
}
private static HashSet<Locale> filterNotMatchingLocale(
HashSet<Locale> appSupportedLocales, HashSet<Locale> assetLocale) {
return appSupportedLocales.stream()
.filter(locale -> matchLanguageInSet(locale, assetLocale))
.collect(Collectors.toCollection(HashSet::new));
}
private static boolean matchLanguageInSet(Locale locale, HashSet<Locale> localesSet) {
if (localesSet.contains(locale)) {
return true;
}
for (Locale l: localesSet) {
if(LocaleList.matchesLanguageAndScript(l, locale)) {
return true;
}
}
return false;
}
private static boolean hasInstallerInfo(Context context, String packageName) {
InstallSourceInfo installSourceInfo;
try {
installSourceInfo = context.getPackageManager().getInstallSourceInfo(packageName);
return installSourceInfo != null;
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Installer info not found for: " + packageName);
}
return false;
}
private static boolean isSystemApp(Context context, String packageName) {
ApplicationInfo applicationInfo;
try {
applicationInfo = context.getPackageManager()
.getApplicationInfoAsUser(packageName, /* flags= */ 0, context.getUserId());
return applicationInfo.isSystemApp();
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Application info not found for: " + packageName);
}
return false;
}
/**
* A class used to store an application's supporting locales.
*/
public static class AppLocaleResult {
public enum LocaleStatus {
UNKNOWN_FAILURE,
NO_SUPPORTED_LANGUAGE_IN_APP,
ASSET_LOCALE_IS_EMPTY,
GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
GET_SUPPORTED_LANGUAGE_FROM_ASSET,
}
LocaleStatus mLocaleStatus;
public HashSet<Locale> mAppSupportedLocales;
public AppLocaleResult(LocaleStatus localeStatus, HashSet<Locale> appSupportedLocales) {
this.mLocaleStatus = localeStatus;
this.mAppSupportedLocales = appSupportedLocales;
}
}
}