1 /* 2 * Copyright (C) 2023 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.regionalpreferences; 18 19 import android.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.icu.text.NumberingSystem; 22 import android.icu.util.ULocale; 23 import android.os.Bundle; 24 import android.os.LocaleList; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceScreen; 30 31 import com.android.internal.app.LocaleHelper; 32 import com.android.internal.app.LocalePicker; 33 import com.android.internal.app.LocaleStore; 34 import com.android.settings.core.BasePreferenceController; 35 import com.android.settings.core.SubSettingLauncher; 36 import com.android.settings.dashboard.DashboardFragment; 37 import com.android.settings.overlay.FeatureFactory; 38 import com.android.settings.widget.TickButtonPreference; 39 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 40 41 import java.util.Locale; 42 43 /** Uses to control the preference UI of numbering system page. */ 44 public class NumberingSystemItemController extends BasePreferenceController { 45 private static final String TAG = NumberingSystemItemController.class.getSimpleName(); 46 private static final String DISPLAY_KEYWORD_NUMBERING_SYSTEM = "numbers"; 47 48 static final String ARG_VALUE_NUMBERING_SYSTEM_SELECT = "arg_value_numbering_system_select"; 49 static final String ARG_VALUE_LANGUAGE_SELECT = "arg_value_language_select"; 50 static final String KEY_SELECTED_LANGUAGE = "key_selected_language"; 51 52 private final MetricsFeatureProvider mMetricsFeatureProvider; 53 54 private String mOption = ""; 55 private String mSelectedLanguage = ""; 56 private DashboardFragment mParentFragment; 57 private PreferenceScreen mPreferenceScreen; 58 NumberingSystemItemController(Context context, Bundle argument)59 public NumberingSystemItemController(Context context, Bundle argument) { 60 super(context, "no_key"); 61 // Initialize the supported languages to LocaleInfos 62 LocaleStore.fillCache(context); 63 mOption = argument.getString( 64 RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, ""); 65 mSelectedLanguage = argument.getString( 66 NumberingSystemItemController.KEY_SELECTED_LANGUAGE, ""); 67 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 68 } 69 70 /** 71 * Displays preference in this controller. 72 */ 73 @Override displayPreference(PreferenceScreen screen)74 public void displayPreference(PreferenceScreen screen) { 75 super.displayPreference(screen); 76 mPreferenceScreen = screen; 77 if (mOption.equals(ARG_VALUE_LANGUAGE_SELECT)) { 78 initLanguageOptionsUi(screen); 79 } else if (mOption.equals(ARG_VALUE_NUMBERING_SYSTEM_SELECT)) { 80 initNumberingSystemOptionsUi(screen, Locale.forLanguageTag(mSelectedLanguage)); 81 } 82 } 83 84 /** 85 * Sets the parent fragment and attaches this controller to the settings lifecycle. 86 * 87 * @param fragment the fragment to use as the parent 88 */ setParentFragment(DashboardFragment fragment)89 public void setParentFragment(DashboardFragment fragment) { 90 mParentFragment = fragment; 91 } 92 93 /** 94 * @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the 95 * Setting should be shown or disabled in Settings. Further, it can be used to produce 96 * appropriate error / warning Slice in the case of unavailability. 97 * </p> 98 * The status is used for the convenience methods: {@link #isAvailable()}, {@link 99 * #isSupported()} 100 * </p> 101 * The inherited class doesn't need to check work profile if android:forWork="true" is set in 102 * preference xml. 103 */ 104 @Override getAvailabilityStatus()105 public int getAvailabilityStatus() { 106 return AVAILABLE; 107 } 108 109 @Override handlePreferenceTreeClick(Preference preference)110 public boolean handlePreferenceTreeClick(Preference preference) { 111 if (mOption.equals(ARG_VALUE_LANGUAGE_SELECT)) { 112 handleLanguageSelect(preference); 113 } else if (mOption.equals(ARG_VALUE_NUMBERING_SYSTEM_SELECT)) { 114 handleNumberSystemSelect(preference); 115 } 116 return true; 117 } 118 initLanguageOptionsUi(PreferenceScreen screen)119 private void initLanguageOptionsUi(PreferenceScreen screen) { 120 // Get current system language list to show on screen. 121 LocaleList localeList = LocaleList.getDefault(); 122 for (int i = 0; i < localeList.size(); i++) { 123 Locale locale = localeList.get(i); 124 LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(locale); 125 if (!localeInfo.hasNumberingSystems()) { 126 continue; 127 } 128 Preference pref = new Preference(mContext); 129 pref.setTitle(LocaleHelper.getDisplayName(locale.stripExtensions(), locale, true)); 130 pref.setKey(locale.toLanguageTag()); 131 pref.setSummary(getNumberingSystem(locale)); 132 screen.addPreference(pref); 133 } 134 } 135 initNumberingSystemOptionsUi(PreferenceScreen screen, Locale targetLocale)136 private void initNumberingSystemOptionsUi(PreferenceScreen screen, Locale targetLocale) { 137 String[] locales = LocalePicker.getSupportedLocales(mContext); 138 for (String localeTag : locales) { 139 Locale supportedLocale = Locale.forLanguageTag(localeTag); 140 if (isSameBaseLocale(targetLocale, supportedLocale)) { 141 TickButtonPreference pref = new TickButtonPreference(mContext); 142 String numberingName = getNumberingSystem(supportedLocale); 143 pref.setTitle(numberingName); 144 String key = supportedLocale.getUnicodeLocaleType( 145 ExtensionTypes.NUMBERING_SYSTEM); 146 pref.setKey(key == null ? RegionalPreferencesDataUtils.DEFAULT_VALUE : key); 147 pref.setSelected(isSameNumberingSystem(targetLocale, supportedLocale)); 148 screen.addPreference(pref); 149 } 150 } 151 } 152 handleLanguageSelect(Preference preference)153 private void handleLanguageSelect(Preference preference) { 154 String selectedLanguage = preference.getKey(); 155 mMetricsFeatureProvider.action(mContext, 156 SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES); 157 final Bundle extra = new Bundle(); 158 extra.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, 159 ARG_VALUE_NUMBERING_SYSTEM_SELECT); 160 extra.putString(KEY_SELECTED_LANGUAGE, selectedLanguage); 161 new SubSettingLauncher(preference.getContext()) 162 .setDestination(NumberingPreferencesFragment.class.getName()) 163 .setSourceMetricsCategory( 164 SettingsEnums.NUMBERING_SYSTEM_LANGUAGE_SELECTION_PREFERENCE) 165 .setArguments(extra) 166 .launch(); 167 } 168 handleNumberSystemSelect(Preference preference)169 private void handleNumberSystemSelect(Preference preference) { 170 for (int i = 0; i < mPreferenceScreen.getPreferenceCount(); i++) { 171 TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(i); 172 Log.i(TAG, "[onPreferenceClick] key is " + pref.getKey()); 173 if (pref.getKey().equals(preference.getKey())) { 174 String numberingSystem = pref.getKey(); 175 pref.setSelected(true); 176 Locale updatedLocale = 177 saveNumberingSystemToLocale(Locale.forLanguageTag(mSelectedLanguage), 178 numberingSystem); 179 mMetricsFeatureProvider.action(mContext, 180 SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES); 181 // After updated locale to framework, this fragment will recreate, 182 // so it needs to update the argument of selected language. 183 Bundle bundle = new Bundle(); 184 bundle.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, 185 ARG_VALUE_NUMBERING_SYSTEM_SELECT); 186 bundle.putString(KEY_SELECTED_LANGUAGE, 187 updatedLocale != null ? updatedLocale.toLanguageTag() : ""); 188 mParentFragment.setArguments(bundle); 189 continue; 190 } 191 pref.setSelected(false); 192 } 193 } 194 saveNumberingSystemToLocale(Locale targetLocale, String value)195 private Locale saveNumberingSystemToLocale(Locale targetLocale, String value) { 196 LocaleList localeList = LocalePicker.getLocales(); 197 Locale[] locales = new Locale[localeList.size()]; 198 Locale updatedLocale = null; 199 for (int i = 0; i < localeList.size(); i++) { 200 Locale locale = localeList.get(i); 201 if (targetLocale.equals(locale)) { 202 if (RegionalPreferencesDataUtils.DEFAULT_VALUE.equals(value)) { 203 value = null; 204 } 205 updatedLocale = new Locale.Builder() 206 .setLocale(locale) 207 .setUnicodeLocaleKeyword(ExtensionTypes.NUMBERING_SYSTEM, value) 208 .build(); 209 locales[i] = updatedLocale; 210 continue; 211 } 212 locales[i] = localeList.get(i); 213 } 214 LocalePicker.updateLocales(new LocaleList(locales)); 215 return updatedLocale; 216 } 217 getNumberingSystem(Locale locale)218 private static String getNumberingSystem(Locale locale) { 219 ULocale uLocale = new ULocale.Builder() 220 .setUnicodeLocaleKeyword(ExtensionTypes.NUMBERING_SYSTEM, 221 NumberingSystem.getInstance(locale).getName()) 222 .build(); 223 return uLocale.getDisplayKeywordValue(DISPLAY_KEYWORD_NUMBERING_SYSTEM, 224 ULocale.forLocale(locale)); 225 } 226 isSameNumberingSystem(Locale locale1, Locale locale2)227 private static boolean isSameNumberingSystem(Locale locale1, Locale locale2) { 228 String name1 = NumberingSystem.getInstance(locale1).getName(); 229 String name2 = NumberingSystem.getInstance(locale2).getName(); 230 return TextUtils.equals(name1, name2); 231 } 232 isSameBaseLocale(Locale locale1, Locale locale2)233 private static boolean isSameBaseLocale(Locale locale1, Locale locale2) { 234 return locale1.stripExtensions().equals(locale2.stripExtensions()); 235 } 236 } 237