• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2025 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.settings.localepicker;
18 
19 import static com.android.settings.localepicker.RegionAndNumberingSystemPickerFragment.EXTRA_APP_PACKAGE_NAME;
20 import static com.android.settings.localepicker.RegionAndNumberingSystemPickerFragment.EXTRA_IS_NUMBERING_SYSTEM;
21 import static com.android.settings.localepicker.RegionAndNumberingSystemPickerFragment.EXTRA_TARGET_LOCALE;
22 
23 import android.app.Activity;
24 import android.app.FragmentManager;
25 import android.app.settings.SettingsEnums;
26 import android.content.Context;
27 import android.os.Bundle;
28 import android.text.TextUtils;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 import androidx.annotation.VisibleForTesting;
35 import androidx.core.app.NotificationCompat;
36 import androidx.preference.Preference;
37 import androidx.preference.PreferenceCategory;
38 import androidx.preference.PreferenceScreen;
39 
40 import com.android.internal.app.AppLocaleCollector;
41 import com.android.internal.app.LocaleStore;
42 import com.android.settings.R;
43 import com.android.settings.applications.manageapplications.ManageApplicationsUtil;
44 import com.android.settings.core.BasePreferenceController;
45 import com.android.settings.core.SubSettingLauncher;
46 import com.android.settings.spa.SpaActivity;
47 import com.android.settingslib.core.instrumentation.Instrumentable;
48 import com.android.settingslib.widget.SelectorWithWidgetPreference;
49 
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Set;
54 import java.util.stream.Collectors;
55 
56 /** A controller for handling supported locale of app. */
57 public class AppLocaleAllListPreferenceController extends
58         BasePreferenceController implements LocaleListSearchCallback {
59     private static final String TAG = "AppLocaleAllListPreferenceController";
60     private static final String KEY_PREFERENCE_CATEGORY_APP_LANGUAGE_ALL_SUPPORTED =
61             "app_language_all_supported_category";
62     private static final String KEY_PREFERENCE_APP_LOCALE_LIST = "app_locale_list";
63     private static final String KEY_PREFERENCE_CATEGORY_ADD_LANGUAGE_ALL_SUPPORTED =
64             "system_language_all_supported_category";
65 
66     private Activity mActivity;
67     @SuppressWarnings("NullAway")
68     private PreferenceCategory mPreferenceCategory;
69     private Set<LocaleStore.LocaleInfo> mLocaleList;
70     private List<LocaleStore.LocaleInfo> mLocaleOptions;
71     private Map<String, Preference> mSupportedPreferences;
72     private boolean mIsCountryMode;
73     private boolean mIsNumberingSystemMode;
74     @Nullable
75     private LocaleStore.LocaleInfo mParentLocale;
76     private AppLocaleCollector mAppLocaleCollector;
77     @SuppressWarnings("NullAway")
78     private String mPackageName;
79 
80     @SuppressWarnings("NullAway")
AppLocaleAllListPreferenceController(@onNull Context context, @NonNull String preferenceKey)81     public AppLocaleAllListPreferenceController(@NonNull Context context,
82             @NonNull String preferenceKey) {
83         super(context, preferenceKey);
84     }
85 
86     @SuppressWarnings("NullAway")
AppLocaleAllListPreferenceController(@onNull Context context, @NonNull String preferenceKey, @Nullable String packageName, boolean isNumberingSystemMode, @NonNull LocaleStore.LocaleInfo parentLocale, @NonNull Activity activity, @NonNull AppLocaleCollector appLocaleCollector)87     public AppLocaleAllListPreferenceController(@NonNull Context context,
88             @NonNull String preferenceKey, @Nullable String packageName,
89             boolean isNumberingSystemMode, @NonNull LocaleStore.LocaleInfo parentLocale,
90             @NonNull Activity activity, @NonNull AppLocaleCollector appLocaleCollector) {
91         super(context, preferenceKey);
92         mPackageName = packageName;
93         mIsNumberingSystemMode = isNumberingSystemMode;
94         mParentLocale = parentLocale;
95         mIsCountryMode = mParentLocale != null;
96         mActivity = activity;
97         mAppLocaleCollector = appLocaleCollector;
98     }
99 
100     @Override
displayPreference(@onNull PreferenceScreen screen)101     public void displayPreference(@NonNull PreferenceScreen screen) {
102         super.displayPreference(screen);
103         mPreferenceCategory = screen.findPreference(
104                 (mIsNumberingSystemMode || mIsCountryMode)
105                         ? KEY_PREFERENCE_CATEGORY_ADD_LANGUAGE_ALL_SUPPORTED
106                         : KEY_PREFERENCE_CATEGORY_APP_LANGUAGE_ALL_SUPPORTED);
107 
108         mAppLocaleCollector = new AppLocaleCollector(mContext, mPackageName);
109         mSupportedPreferences = new ArrayMap<>();
110         mLocaleOptions = new ArrayList<>();
111         updatePreferences();
112     }
113 
updatePreferences()114     private void updatePreferences() {
115         if (mPreferenceCategory == null) {
116             Log.d(TAG, "updatePreferences, mPreferenceCategory is null");
117             return;
118         }
119 
120         List<LocaleStore.LocaleInfo> result = LocaleUtils.getSortedLocaleList(
121                 getSupportedLocaleList(), mIsCountryMode);
122         if (mIsCountryMode) {
123             mPreferenceCategory.setTitle(
124                     mContext.getString(R.string.all_supported_locales_regions_title));
125         }
126         final Map<String, Preference> existingSupportedPreferences = mSupportedPreferences;
127         mSupportedPreferences = new ArrayMap<>();
128         setupSupportedPreference(result, existingSupportedPreferences);
129         for (Preference pref : existingSupportedPreferences.values()) {
130             mPreferenceCategory.removePreference(pref);
131         }
132     }
133 
134     @Override
onSearchListChanged(@onNull List<LocaleStore.LocaleInfo> newList, @Nullable CharSequence prefix)135     public void onSearchListChanged(@NonNull List<LocaleStore.LocaleInfo> newList,
136             @Nullable CharSequence prefix) {
137         mPreferenceCategory.removeAll();
138         mSupportedPreferences.clear();
139         final Map<String, Preference> existingSupportedPreferences = mSupportedPreferences;
140         List<LocaleStore.LocaleInfo> sortedList = getSupportedLocaleList();
141         newList = LocaleUtils.getSortedLocaleFromSearchList(newList, sortedList, mIsCountryMode);
142         setupSupportedPreference(newList, existingSupportedPreferences);
143     }
144 
145     @VisibleForTesting
setupSupportedPreference(List<LocaleStore.LocaleInfo> localeInfoList, Map<String, Preference> existingSupportedPreferences)146     void setupSupportedPreference(List<LocaleStore.LocaleInfo> localeInfoList,
147             Map<String, Preference> existingSupportedPreferences) {
148         if (mIsNumberingSystemMode) {
149             mPreferenceCategory.setTitle("");
150         }
151 
152         for (LocaleStore.LocaleInfo locale : localeInfoList) {
153             Preference pref = existingSupportedPreferences.remove(locale.getId());
154             if (pref == null) {
155                 pref = new Preference(mContext);
156                 mPreferenceCategory.addPreference(pref);
157                 setupPreference(pref, locale);
158             }
159         }
160         mPreferenceCategory.setVisible(mPreferenceCategory.getPreferenceCount() > 0);
161     }
162 
setupPreference(Preference pref, LocaleStore.LocaleInfo locale)163     private void setupPreference(Preference pref, LocaleStore.LocaleInfo locale) {
164         String localeName = mIsCountryMode ? locale.getFullCountryNameNative()
165                 : locale.getFullNameNative();
166         pref.setTitle(localeName);
167         pref.setKey(locale.toString());
168         pref.setOnPreferenceClickListener(clickedPref -> {
169             // TODO: b/388199937 - Switch to correct fragment.
170             Log.d(TAG, "setupPreference: mIsCountryMode = " + mIsCountryMode);
171             switchFragment(mContext, locale, shouldShowAppLanguage(locale));
172             mActivity.finish();
173             return true;
174         });
175         mSupportedPreferences.put(locale.getId(), pref);
176     }
177 
178     @Override
getAvailabilityStatus()179     public int getAvailabilityStatus() {
180         return AVAILABLE;
181     }
182 
183     @VisibleForTesting
switchFragment(Context context, LocaleStore.LocaleInfo localeInfo, boolean shouldShowAppLanguage)184     void switchFragment(Context context, LocaleStore.LocaleInfo localeInfo,
185             boolean shouldShowAppLanguage) {
186         if (shouldShowAppLanguage) {
187             LocaleUtils.onLocaleSelected(mContext, localeInfo, mPackageName);
188         } else {
189             String extraKey = EXTRA_TARGET_LOCALE;
190             String fragmentName = RegionAndNumberingSystemPickerFragment.class.getCanonicalName();
191             final Bundle extra = new Bundle();
192             extra.putSerializable(extraKey, localeInfo);
193             extra.putBoolean(EXTRA_IS_NUMBERING_SYSTEM, localeInfo.hasNumberingSystems());
194             extra.putString(EXTRA_APP_PACKAGE_NAME, mPackageName);
195             new SubSettingLauncher(context)
196                     .setDestination(fragmentName)
197                     .setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
198                     .setArguments(extra)
199                     .launch();
200         }
201     }
202 
203     @VisibleForTesting
shouldShowAppLanguage(LocaleStore.LocaleInfo localeInfo)204     boolean shouldShowAppLanguage(LocaleStore.LocaleInfo localeInfo) {
205         boolean isSystemLocale = localeInfo.isSystemLocale();
206         boolean isRegionLocale = localeInfo.getParent() != null;
207         boolean mayHaveDifferentNumberingSystem = localeInfo.hasNumberingSystems();
208         mLocaleList = mAppLocaleCollector.getSupportedLocaleList(localeInfo,
209                 false, (localeInfo != null));
210         Log.d(TAG,
211                 "shouldShowAppLanguage: isSystemLocale = " + isSystemLocale + ", isRegionLocale = "
212                         + isRegionLocale + ", mayHaveDifferentNumberingSystem = "
213                         + mayHaveDifferentNumberingSystem + ", isNumberingMode = "
214                         + mIsNumberingSystemMode);
215 
216         return mLocaleList.size() == 1 || isSystemLocale || mIsNumberingSystemMode
217                 || (isRegionLocale && !mayHaveDifferentNumberingSystem);
218     }
219 
getSupportedLocaleList()220     protected List<LocaleStore.LocaleInfo> getSupportedLocaleList() {
221         setupLocaleList();
222         if (mLocaleList != null && !mLocaleList.isEmpty()) {
223             mLocaleOptions.addAll(
224                     mLocaleList.stream().filter(localeInfo -> !localeInfo.isSuggested()).collect(
225                             Collectors.toList()));
226         } else {
227             Log.d(TAG, "Can not get supported locales because the locale list is null or empty.");
228         }
229         return mLocaleOptions;
230     }
231 
setupLocaleList()232     private void setupLocaleList() {
233         mLocaleList = mAppLocaleCollector.getSupportedLocaleList(mParentLocale,
234                 false, mIsCountryMode);
235         mLocaleOptions.clear();
236     }
237 
238     @Override
getPreferenceKey()239     public @NonNull String getPreferenceKey() {
240         return KEY_PREFERENCE_APP_LOCALE_LIST;
241     }
242 }
243