1 /* 2 * Copyright (C) 2021 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 package com.android.settings.applications.appinfo; 17 18 import static com.android.settings.widget.EntityHeaderController.ActionType; 19 20 import android.app.Activity; 21 import android.app.LocaleManager; 22 import android.app.settings.SettingsEnums; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.os.Bundle; 27 import android.os.LocaleList; 28 import android.os.UserHandle; 29 import android.util.FeatureFlagUtils; 30 import android.util.Log; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.TextView; 35 36 import androidx.annotation.Nullable; 37 import androidx.preference.Preference; 38 39 import com.android.internal.app.LocaleHelper; 40 import com.android.settings.R; 41 import com.android.settings.SettingsPreferenceFragment; 42 import com.android.settings.Utils; 43 import com.android.settings.applications.AppInfoBase; 44 import com.android.settings.applications.AppLocaleUtil; 45 import com.android.settings.widget.EntityHeaderController; 46 import com.android.settingslib.applications.AppUtils; 47 import com.android.settingslib.widget.LayoutPreference; 48 49 import java.util.Locale; 50 51 /** 52 * TODO(b/223503670): Implement the unittest. 53 * A fragment to show the current app locale info. 54 */ 55 public class AppLocaleDetails extends SettingsPreferenceFragment { 56 private static final String TAG = "AppLocaleDetails"; 57 58 private static final String KEY_APP_DESCRIPTION = "app_locale_description"; 59 private static final String KEY_APP_DISCLAIMER = "app_locale_disclaimer"; 60 61 private boolean mCreated = false; 62 private String mPackageName; 63 private LayoutPreference mPrefOfDescription; 64 private Preference mPrefOfDisclaimer; 65 private ApplicationInfo mApplicationInfo; 66 @Nullable private String mParentLocale; 67 68 /** 69 * Create a instance of AppLocaleDetails. 70 * @param packageName Indicates which application need to show the locale picker. 71 * @param uid User id. 72 */ newInstance(String packageName, int uid)73 public static AppLocaleDetails newInstance(String packageName, int uid) { 74 AppLocaleDetails appLocaleDetails = new AppLocaleDetails(); 75 Bundle bundle = new Bundle(); 76 bundle.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName); 77 bundle.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); 78 appLocaleDetails.setArguments(bundle); 79 return appLocaleDetails; 80 } 81 82 @Override onCreate(Bundle savedInstanceState)83 public void onCreate(Bundle savedInstanceState) { 84 super.onCreate(savedInstanceState); 85 Bundle bundle = getArguments(); 86 mPackageName = bundle.getString(AppInfoBase.ARG_PACKAGE_NAME, ""); 87 if (mPackageName.isEmpty()) { 88 Log.d(TAG, "There is no package name."); 89 finish(); 90 } 91 int uid = bundle.getInt(AppInfoBase.ARG_PACKAGE_UID, getContext().getUserId()); 92 93 addPreferencesFromResource(R.xml.app_locale_details); 94 mPrefOfDescription = getPreferenceScreen().findPreference(KEY_APP_DESCRIPTION); 95 mPrefOfDisclaimer = getPreferenceScreen().findPreference(KEY_APP_DISCLAIMER); 96 mApplicationInfo = getApplicationInfo(mPackageName, uid); 97 setDisclaimerPreference(); 98 } 99 100 // Override here so we don't have an empty screen 101 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)102 public View onCreateView(LayoutInflater inflater, 103 ViewGroup container, 104 Bundle savedInstanceState) { 105 // if we don't have a package, show a page saying this is unsupported 106 if (mPackageName.isEmpty()) { 107 return inflater.inflate(R.layout.manage_applications_apps_unsupported, null); 108 } 109 return super.onCreateView(inflater, container, savedInstanceState); 110 } 111 112 @Override onResume()113 public void onResume() { 114 super.onResume(); 115 refreshUi(); 116 final Activity activity = getActivity(); 117 if (mParentLocale != null) { 118 activity.setTitle(mParentLocale); 119 } else { 120 activity.setTitle(R.string.app_locale_picker_title); 121 } 122 } 123 refreshUi()124 private void refreshUi() { 125 setDescription(); 126 } 127 128 @Override getMetricsCategory()129 public int getMetricsCategory() { 130 return SettingsEnums.APPS_LOCALE_LIST; 131 } 132 133 @Override onActivityCreated(Bundle savedInstanceState)134 public void onActivityCreated(Bundle savedInstanceState) { 135 super.onActivityCreated(savedInstanceState); 136 if (mCreated) { 137 Log.w(TAG, "onActivityCreated: ignoring duplicate call"); 138 return; 139 } 140 mCreated = true; 141 if (mPackageName == null) { 142 return; 143 } 144 // Creates a head icon button of app on this page. 145 final Activity activity = getActivity(); 146 final Preference pref = EntityHeaderController 147 .newInstance(activity, this, null /* header */) 148 .setIcon(Utils.getBadgedIcon(getContext(), mApplicationInfo)) 149 .setLabel(mApplicationInfo.loadLabel(getContext().getPackageManager())) 150 .setIsInstantApp(AppUtils.isInstant(mApplicationInfo)) 151 .setPackageName(mPackageName) 152 .setUid(mApplicationInfo.uid) 153 .setHasAppInfoLink(true) 154 .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE) 155 .setOrder(10) 156 .done(getPrefContext()); 157 getPreferenceScreen().addPreference(pref); 158 } 159 setDisclaimerPreference()160 private void setDisclaimerPreference() { 161 if (FeatureFlagUtils.isEnabled( 162 getContext(), FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED)) { 163 mPrefOfDisclaimer.setVisible(false); 164 } 165 } 166 setDescription()167 private void setDescription() { 168 int res = getAppDescription(); 169 if (res != -1) { 170 mPrefOfDescription.setVisible(true); 171 TextView description = (TextView) mPrefOfDescription.findViewById(R.id.description); 172 description.setText(getContext().getString(res)); 173 } 174 } 175 getApplicationInfo(String packageName, int userId)176 private ApplicationInfo getApplicationInfo(String packageName, int userId) { 177 ApplicationInfo applicationInfo; 178 try { 179 applicationInfo = getContext().getPackageManager() 180 .getApplicationInfoAsUser(packageName, /* flags= */ 0, userId); 181 return applicationInfo; 182 } catch (PackageManager.NameNotFoundException e) { 183 Log.w(TAG, "Application info not found for: " + packageName); 184 return null; 185 } 186 } 187 getAppDescription()188 private int getAppDescription() { 189 LocaleList packageLocaleList = AppLocaleUtil.getPackageLocales(getContext(), mPackageName); 190 String[] assetLocaleList = AppLocaleUtil.getAssetLocales(getContext(), mPackageName); 191 // TODO add apended url string, "Learn more", to these both sentenses. 192 if ((packageLocaleList != null && packageLocaleList.isEmpty()) 193 || (packageLocaleList == null && assetLocaleList.length == 0)) { 194 return R.string.desc_no_available_supported_locale; 195 } 196 return -1; 197 } 198 199 /** Gets per app's default locale */ getAppDefaultLocale(Context context, String packageName)200 public static Locale getAppDefaultLocale(Context context, String packageName) { 201 LocaleManager localeManager = context.getSystemService(LocaleManager.class); 202 try { 203 LocaleList localeList = (localeManager == null) 204 ? null : localeManager.getApplicationLocales(packageName); 205 return localeList == null ? null : localeList.get(0); 206 } catch (IllegalArgumentException e) { 207 Log.w(TAG, "package name : " + packageName + " is not correct. " + e); 208 } 209 return null; 210 } 211 212 /** 213 * TODO (b209962418) Do a performance test to low end device. 214 * @return Return the summary to show the current app's language. 215 */ getSummary(Context context, ApplicationInfo app)216 public static CharSequence getSummary(Context context, ApplicationInfo app) { 217 final UserHandle userHandle = UserHandle.getUserHandleForUid(app.uid); 218 final Context contextAsUser = context.createContextAsUser(userHandle, 0); 219 Locale appLocale = getAppDefaultLocale(contextAsUser, app.packageName); 220 if (appLocale == null) { 221 return context.getString(R.string.preference_of_system_locale_summary); 222 } else { 223 return LocaleHelper.getDisplayName(appLocale.stripExtensions(), appLocale, true); 224 } 225 } 226 setParentLocale(@ullable String localeName)227 public void setParentLocale(@Nullable String localeName) { 228 mParentLocale = localeName; 229 } 230 } 231