fix: 引入Settings的Module

This commit is contained in:
2024-12-10 14:57:24 +08:00
parent ad8fc8731d
commit df105485bd
6934 changed files with 896168 additions and 2 deletions

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.annotation.StringDef;
/** Provides necessary value of language tag to Regional Preferences. */
public class ExtensionTypes {
public static final String CALENDAR = "ca";
public static final String FIRST_DAY_OF_WEEK = "fw";
public static final String NUMBERING_SYSTEM = "nu";
public static final String TEMPERATURE_UNIT = "mu";
@StringDef({
FIRST_DAY_OF_WEEK,
CALENDAR,
TEMPERATURE_UNIT,
NUMBERING_SYSTEM
})
public @interface Values {}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.settings.regionalpreferences;
import android.content.Context;
import android.provider.Settings;
import androidx.core.text.util.LocalePreferences;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import java.util.Locale;
/** A controller for the entry of First Day of Week's page */
public class FirstDayOfWeekController extends BasePreferenceController {
private static final String TAG = FirstDayOfWeekController.class.getSimpleName();
public FirstDayOfWeekController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
/**
* @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
* Setting should be shown or disabled in Settings. Further, it can be used to produce
* appropriate error / warning Slice in the case of unavailability.
* </p>
* The status is used for the convenience methods: {@link #isAvailable()}, {@link
* #isSupported()}
* </p>
* The inherited class doesn't need to check work profile if android:forWork="true" is set in
* preference xml.
*/
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
String record = Settings.System.getString(
mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
String result = "";
if (record != null) {
result = LocalePreferences.getFirstDayOfWeek(Locale.forLanguageTag(record), false);
}
if (result.isEmpty()) {
result = LocalePreferences.getFirstDayOfWeek(false);
}
return result.isEmpty()
? mContext.getString(R.string.default_string_of_regional_preference)
: RegionalPreferencesDataUtils.dayConverter(mContext, result);
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.content.Context;
import android.util.Log;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.widget.PreferenceCategoryController;
/** Category preference controller for first day of week preferences. */
public class FirstDayOfWeekItemCategoryController extends PreferenceCategoryController {
private static final String LOG_TAG = "FirstDayOfWeekItemCategoryController";
private static final String KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM =
"first_day_of_week_item_category";
private static final String KEY_PREFERENCE_FIRST_DAY_OF_WEEK_ITEM =
"first_day_of_week_item_list";
private PreferenceCategory mPreferenceCategory;
private FirstDayOfWeekItemListController mFirstDayOfWeekItemListController;
public FirstDayOfWeekItemCategoryController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceCategory = screen.findPreference(KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM);
if (mPreferenceCategory == null) {
Log.d(LOG_TAG, "displayPreference(), Can not find the category.");
return;
}
mPreferenceCategory.setVisible(isAvailable());
mFirstDayOfWeekItemListController = new FirstDayOfWeekItemListController(mContext,
KEY_PREFERENCE_FIRST_DAY_OF_WEEK_ITEM);
mFirstDayOfWeekItemListController.displayPreference(screen);
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/** Main fragment to display first day of week. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class FirstDayOfWeekItemFragment extends DashboardFragment {
private static final String LOG_TAG = "FirstDayOfWeekItemFragment";
private static final String KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM =
"first_day_of_week_item_category";
@Override
protected int getPreferenceScreenResId() {
return R.xml.regional_preferences_first_day_of_week;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.FIRST_DAY_OF_WEEK_PREFERENCE;
}
@Override
protected String getLogTag() {
return LOG_TAG;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new FirstDayOfWeekItemCategoryController(context,
KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM));
return controllers;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.regional_preferences_first_day_of_week);
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
/** A controller for handling all first day of week preferences. */
public class FirstDayOfWeekItemListController extends
RegionalPreferenceListBasePreferenceController {
private static final String KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM =
"first_day_of_week_item_category";
private static final String KEY_PREFERENCE_FIRST_DAY_OF_WEEK_ITEM =
"first_day_of_week_item_list";
public FirstDayOfWeekItemListController(Context context, String key) {
super(context, key);
}
@Override
protected String getPreferenceTitle(String item) {
return RegionalPreferencesDataUtils.dayConverter(mContext, item);
}
@Override
protected String getPreferenceCategoryKey() {
return KEY_PREFERENCE_CATEGORY_FIRST_DAY_OF_WEEK_ITEM;
}
@Override
public String getPreferenceKey() {
return KEY_PREFERENCE_FIRST_DAY_OF_WEEK_ITEM;
}
@Override
protected String getExtensionTypes() {
return ExtensionTypes.FIRST_DAY_OF_WEEK;
}
@Override
protected String[] getUnitValues() {
return mContext.getResources().getStringArray(R.array.first_day_of_week);
}
@Override
protected int getMetricsActionKey() {
return SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK;
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.internal.app.LocaleHelper;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/** Provides options of numbering system to each language. */
public class NumberingPreferencesFragment extends DashboardFragment {
/** Initializes variables. */
@VisibleForTesting
String initTitle() {
String option = getArguments().getString(
RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, "");
if (option.isEmpty()) {
Log.d(getLogTag(), "Option is empty.");
return "";
}
Log.i(getLogTag(), "[NumberingPreferencesFragment] option is " + option);
if (option.equals(NumberingSystemItemController.ARG_VALUE_LANGUAGE_SELECT)) {
return getContext().getString(R.string.numbers_preferences_title);
} else if (option.equals(NumberingSystemItemController.ARG_VALUE_NUMBERING_SYSTEM_SELECT)) {
String selectedLanguage = getArguments().getString(
NumberingSystemItemController.KEY_SELECTED_LANGUAGE, "");
if (selectedLanguage.isEmpty()) {
Log.w(getLogTag(), "No selected language.");
return "";
}
Locale locale = Locale.forLanguageTag(selectedLanguage);
return LocaleHelper.getDisplayName(locale.stripExtensions(), locale, true);
}
Log.w(getLogTag(), "Incorrect option : " + option);
return "";
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String title = initTitle();
if (initTitle().isEmpty()) {
finish();
} else {
getActivity().setTitle(title);
}
}
/**
* Get a list of {@link AbstractPreferenceController} for this fragment.
*/
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
NumberingSystemItemController controller =
new NumberingSystemItemController(context, getArguments());
controller.setParentFragment(this);
List<AbstractPreferenceController> listControllers = new ArrayList<>();
listControllers.add(controller);
return listControllers;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.regional_preference_numbering_system_page;
}
/**
* Get the tag string for logging.
*/
@Override
protected String getLogTag() {
return NumberingPreferencesFragment.class.getSimpleName();
}
@Override
public int getMetricsCategory() {
String option = getArguments().getString(
RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, "");
if (option.equals(NumberingSystemItemController.ARG_VALUE_LANGUAGE_SELECT)) {
return SettingsEnums.NUMBERING_SYSTEM_LANGUAGE_SELECTION_PREFERENCE;
} else {
return SettingsEnums.NUMBERING_SYSTEM_NUMBER_FORMAT_SELECTION_PREFERENCE;
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.settings.regionalpreferences;
import android.content.Context;
import android.os.LocaleList;
import com.android.internal.app.LocaleStore;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
/** A controller for the entry of Numbering System's page */
public class NumberingSystemController extends BasePreferenceController {
private static final String TAG = NumberingSystemController.class.getSimpleName();
private LocaleList mLocaleList;
public NumberingSystemController(Context context, String preferenceKey) {
super(context, preferenceKey);
// Initialize the supported languages to LocaleInfos
LocaleStore.fillCache(context);
mLocaleList = getNumberingSystemLocale();
}
/**
* @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
* Setting should be shown or disabled in Settings. Further, it can be used to produce
* appropriate error / warning Slice in the case of unavailability.
* </p>
* The status is used for the convenience methods: {@link #isAvailable()}, {@link
* #isSupported()}
* </p>
* The inherited class doesn't need to check work profile if android:forWork="true" is set in
* preference xml.
*/
@Override
public int getAvailabilityStatus() {
return mLocaleList.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
}
private static LocaleList getNumberingSystemLocale() {
LocaleList localeList = LocaleList.getDefault();
Set<Locale> localesHasNumberingSystems = new HashSet<>();
for (int i = 0; i < localeList.size(); i++) {
Locale locale = localeList.get(i);
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(locale);
if (localeInfo.hasNumberingSystems()) {
localesHasNumberingSystems.add(locale);
}
}
return convertToLocaleList(localesHasNumberingSystems);
}
private static LocaleList convertToLocaleList(Set<Locale> locales) {
if (locales.isEmpty()) {
return LocaleList.getEmptyLocaleList();
}
return new LocaleList(locales.stream().toArray(Locale[]::new));
}
@Override
public CharSequence getSummary() {
return new LocaleFeatureProviderImpl().getLocaleNames(getNumberingSystemLocale());
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.icu.text.NumberingSystem;
import android.icu.util.ULocale;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.TextUtils;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.app.LocaleHelper;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocaleStore;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.TickButtonPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.Locale;
/** Uses to control the preference UI of numbering system page. */
public class NumberingSystemItemController extends BasePreferenceController {
private static final String TAG = NumberingSystemItemController.class.getSimpleName();
private static final String DISPLAY_KEYWORD_NUMBERING_SYSTEM = "numbers";
static final String ARG_VALUE_NUMBERING_SYSTEM_SELECT = "arg_value_numbering_system_select";
static final String ARG_VALUE_LANGUAGE_SELECT = "arg_value_language_select";
static final String KEY_SELECTED_LANGUAGE = "key_selected_language";
private final MetricsFeatureProvider mMetricsFeatureProvider;
private String mOption = "";
private String mSelectedLanguage = "";
private DashboardFragment mParentFragment;
private PreferenceScreen mPreferenceScreen;
public NumberingSystemItemController(Context context, Bundle argument) {
super(context, "no_key");
// Initialize the supported languages to LocaleInfos
LocaleStore.fillCache(context);
mOption = argument.getString(
RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE, "");
mSelectedLanguage = argument.getString(
NumberingSystemItemController.KEY_SELECTED_LANGUAGE, "");
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
}
/**
* Displays preference in this controller.
*/
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceScreen = screen;
if (mOption.equals(ARG_VALUE_LANGUAGE_SELECT)) {
initLanguageOptionsUi(screen);
} else if (mOption.equals(ARG_VALUE_NUMBERING_SYSTEM_SELECT)) {
initNumberingSystemOptionsUi(screen, Locale.forLanguageTag(mSelectedLanguage));
}
}
/**
* Sets the parent fragment and attaches this controller to the settings lifecycle.
*
* @param fragment the fragment to use as the parent
*/
public void setParentFragment(DashboardFragment fragment) {
mParentFragment = fragment;
}
/**
* @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
* Setting should be shown or disabled in Settings. Further, it can be used to produce
* appropriate error / warning Slice in the case of unavailability.
* </p>
* The status is used for the convenience methods: {@link #isAvailable()}, {@link
* #isSupported()}
* </p>
* The inherited class doesn't need to check work profile if android:forWork="true" is set in
* preference xml.
*/
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (mOption.equals(ARG_VALUE_LANGUAGE_SELECT)) {
handleLanguageSelect(preference);
} else if (mOption.equals(ARG_VALUE_NUMBERING_SYSTEM_SELECT)) {
handleNumberSystemSelect(preference);
}
return true;
}
private void initLanguageOptionsUi(PreferenceScreen screen) {
// Get current system language list to show on screen.
LocaleList localeList = LocaleList.getDefault();
for (int i = 0; i < localeList.size(); i++) {
Locale locale = localeList.get(i);
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(locale);
if (!localeInfo.hasNumberingSystems()) {
continue;
}
Preference pref = new Preference(mContext);
pref.setTitle(LocaleHelper.getDisplayName(locale.stripExtensions(), locale, true));
pref.setKey(locale.toLanguageTag());
pref.setSummary(getNumberingSystem(locale));
screen.addPreference(pref);
}
}
private void initNumberingSystemOptionsUi(PreferenceScreen screen, Locale targetLocale) {
String[] locales = LocalePicker.getSupportedLocales(mContext);
for (String localeTag : locales) {
Locale supportedLocale = Locale.forLanguageTag(localeTag);
if (isSameBaseLocale(targetLocale, supportedLocale)) {
TickButtonPreference pref = new TickButtonPreference(mContext);
String numberingName = getNumberingSystem(supportedLocale);
pref.setTitle(numberingName);
String key = supportedLocale.getUnicodeLocaleType(
ExtensionTypes.NUMBERING_SYSTEM);
pref.setKey(key == null ? RegionalPreferencesDataUtils.DEFAULT_VALUE : key);
pref.setSelected(isSameNumberingSystem(targetLocale, supportedLocale));
screen.addPreference(pref);
}
}
}
private void handleLanguageSelect(Preference preference) {
String selectedLanguage = preference.getKey();
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_CHOOSE_LANGUAGE_FOR_NUMBERS_PREFERENCES);
final Bundle extra = new Bundle();
extra.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE,
ARG_VALUE_NUMBERING_SYSTEM_SELECT);
extra.putString(KEY_SELECTED_LANGUAGE, selectedLanguage);
new SubSettingLauncher(preference.getContext())
.setDestination(NumberingPreferencesFragment.class.getName())
.setSourceMetricsCategory(
SettingsEnums.NUMBERING_SYSTEM_LANGUAGE_SELECTION_PREFERENCE)
.setArguments(extra)
.launch();
}
private void handleNumberSystemSelect(Preference preference) {
for (int i = 0; i < mPreferenceScreen.getPreferenceCount(); i++) {
TickButtonPreference pref = (TickButtonPreference) mPreferenceScreen.getPreference(i);
Log.i(TAG, "[onPreferenceClick] key is " + pref.getKey());
if (pref.getKey().equals(preference.getKey())) {
String numberingSystem = pref.getKey();
pref.setSelected(true);
Locale updatedLocale =
saveNumberingSystemToLocale(Locale.forLanguageTag(mSelectedLanguage),
numberingSystem);
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_SET_NUMBERS_PREFERENCES);
// After updated locale to framework, this fragment will recreate,
// so it needs to update the argument of selected language.
Bundle bundle = new Bundle();
bundle.putString(RegionalPreferencesEntriesFragment.ARG_KEY_REGIONAL_PREFERENCE,
ARG_VALUE_NUMBERING_SYSTEM_SELECT);
bundle.putString(KEY_SELECTED_LANGUAGE,
updatedLocale != null ? updatedLocale.toLanguageTag() : "");
mParentFragment.setArguments(bundle);
continue;
}
pref.setSelected(false);
}
}
private Locale saveNumberingSystemToLocale(Locale targetLocale, String value) {
LocaleList localeList = LocalePicker.getLocales();
Locale[] locales = new Locale[localeList.size()];
Locale updatedLocale = null;
for (int i = 0; i < localeList.size(); i++) {
Locale locale = localeList.get(i);
if (targetLocale.equals(locale)) {
if (RegionalPreferencesDataUtils.DEFAULT_VALUE.equals(value)) {
value = null;
}
updatedLocale = new Locale.Builder()
.setLocale(locale)
.setUnicodeLocaleKeyword(ExtensionTypes.NUMBERING_SYSTEM, value)
.build();
locales[i] = updatedLocale;
continue;
}
locales[i] = localeList.get(i);
}
LocalePicker.updateLocales(new LocaleList(locales));
return updatedLocale;
}
private static String getNumberingSystem(Locale locale) {
ULocale uLocale = new ULocale.Builder()
.setUnicodeLocaleKeyword(ExtensionTypes.NUMBERING_SYSTEM,
NumberingSystem.getInstance(locale).getName())
.build();
return uLocale.getDisplayKeywordValue(DISPLAY_KEYWORD_NUMBERING_SYSTEM,
ULocale.forLocale(locale));
}
private static boolean isSameNumberingSystem(Locale locale1, Locale locale2) {
String name1 = NumberingSystem.getInstance(locale1).getName();
String name2 = NumberingSystem.getInstance(locale2).getName();
return TextUtils.equals(name1, name2);
}
private static boolean isSameBaseLocale(Locale locale1, Locale locale2) {
return locale1.stripExtensions().equals(locale2.stripExtensions());
}
}

View File

@@ -0,0 +1,7 @@
allenwtsu@google.com
calvinpan@google.com
danielwbhuang@google.com
goldmanj@google.com
joshhou@google.com
zoeychen@google.com
tomhsu@google.com

View File

@@ -0,0 +1,74 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.FooterPreference;
/**
* Preference controller for regional preference footer.
*/
public class RegionalFooterPreferenceController extends BasePreferenceController {
private static final String TAG = "RegionalFooterPreferenceController";
public RegionalFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE_UNSEARCHABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
FooterPreference footerPreference = screen.findPreference(getPreferenceKey());
setupFooterPreference(footerPreference);
}
@VisibleForTesting
void setupFooterPreference(FooterPreference footerPreference) {
if (footerPreference != null) {
footerPreference.setLearnMoreAction(v -> openLocaleLearnMoreLink());
footerPreference.setLearnMoreText(mContext.getString(
R.string.desc_regional_pref_footer_learn_more));
}
}
private void openLocaleLearnMoreLink() {
Intent intent = HelpUtils.getHelpIntent(
mContext,
mContext.getString(R.string.regional_pref_footer_learn_more_link),
mContext.getClass().getName());
if (intent != null) {
mContext.startActivity(intent);
} else {
Log.w(TAG, "HelpIntent is null");
}
}
}

View File

@@ -0,0 +1,106 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.TickButtonPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** A base controller for handling all regional preferences controllers. */
public abstract class RegionalPreferenceListBasePreferenceController extends
BasePreferenceController {
private final MetricsFeatureProvider mMetricsFeatureProvider;
private PreferenceCategory mPreferenceCategory;
public RegionalPreferenceListBasePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceCategory = screen.findPreference(getPreferenceCategoryKey());
initPreferences();
}
private void initPreferences() {
if (mPreferenceCategory == null) {
return;
}
String[] unitValues = getUnitValues();
for (int i = 0; i < unitValues.length; i++) {
TickButtonPreference pref = new TickButtonPreference(mContext);
mPreferenceCategory.addPreference(pref);
final String item = unitValues[i];
final String value = RegionalPreferencesDataUtils.getDefaultUnicodeExtensionData(
mContext, getExtensionTypes());
pref.setTitle(getPreferenceTitle(item));
pref.setKey(item);
pref.setOnPreferenceClickListener(clickedPref -> {
setSelected(pref);
RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(),
item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)
? null : item);
String metrics =
getMetricsActionKey() == SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK ? ""
: getPreferenceTitle(value) + " > " + getPreferenceTitle(item);
mMetricsFeatureProvider.action(mContext, getMetricsActionKey(), metrics);
return true;
});
pref.setSelected(!value.isEmpty() && item.equals(value));
}
}
private void setSelected(TickButtonPreference preference) {
for (int i = 0; i < mPreferenceCategory.getPreferenceCount(); i++) {
TickButtonPreference pref = (TickButtonPreference) mPreferenceCategory.getPreference(i);
if (pref.getKey().equals(preference.getKey())) {
pref.setSelected(true);
continue;
}
pref.setSelected(false);
}
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
protected abstract String getPreferenceTitle(String item);
protected abstract String getPreferenceCategoryKey();
protected abstract String getExtensionTypes();
protected abstract String[] getUnitValues();
protected abstract int getMetricsActionKey();
}

View File

@@ -0,0 +1,48 @@
/*
* 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.settings.regionalpreferences;
import android.content.Context;
import android.os.SystemProperties;
import com.android.settings.core.BasePreferenceController;
/** A controller for the entry of Regional preferences */
public class RegionalPreferencesController extends BasePreferenceController {
// This is a feature flag and will be removed after feature completed.
static final String FEATURE_PROPERTY = "i18n-feature-locale-preference";
public RegionalPreferencesController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
/**
* @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
* Setting should be shown or disabled in Settings. Further, it can be used to produce
* appropriate error / warning Slice in the case of unavailability.
* </p>
* The status is used for the convenience methods: {@link #isAvailable()}, {@link
* #isSupported()}
* </p>
* The inherited class doesn't need to check work profile if android:forWork="true" is set in
* preference xml.
*/
@Override
public int getAvailabilityStatus() {
return SystemProperties.getBoolean(FEATURE_PROPERTY, true)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.content.Context;
import android.icu.util.ULocale;
import android.os.LocaleList;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.core.text.util.LocalePreferences;
import com.android.internal.app.LocalePicker;
import com.android.settings.R;
import java.util.Locale;
/** Provides utils for regional preferences. */
public class RegionalPreferencesDataUtils {
static final String DEFAULT_VALUE = "default";
static String getDefaultUnicodeExtensionData(Context contxt, String type) {
// 1. Check cache data in Settings provider.
String record = Settings.System.getString(
contxt.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
String result = "";
if (!TextUtils.isEmpty(record)) {
result = Locale.forLanguageTag(record).getUnicodeLocaleType(type);
}
// 2. Check cache data in default Locale(ICU lib).
if (TextUtils.isEmpty(result)) {
result = Locale.getDefault(Locale.Category.FORMAT).getUnicodeLocaleType(type);
}
return result == null ? DEFAULT_VALUE : result;
}
static void savePreference(Context context, String type, String value) {
saveToSettingsProvider(context, type, value);
saveToSystem(type, value);
}
private static void saveToSettingsProvider(Context context, String type, String value) {
String record = Settings.System.getString(
context.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
record = record == null ? "" : record;
Settings.System.putString(
context.getContentResolver(),
Settings.System.LOCALE_PREFERENCES,
addUnicodeKeywordToLocale(record, type, value).toLanguageTag());
}
private static void saveToSystem(String type, String value) {
LocaleList localeList = LocaleList.getDefault();
Locale[] resultLocales = new Locale[localeList.size()];
for (int i = 0; i < localeList.size(); i++) {
resultLocales[i] = addUnicodeKeywordToLocale(localeList.get(i), type, value);
}
LocalePicker.updateLocales(new LocaleList(resultLocales));
}
private static Locale addUnicodeKeywordToLocale(Locale locale, String type, String value) {
return new Locale.Builder()
.setLocale(locale)
.setUnicodeLocaleKeyword(type, value)
.build();
}
private static Locale addUnicodeKeywordToLocale(String languageTag, String type, String value) {
return addUnicodeKeywordToLocale(Locale.forLanguageTag(languageTag), type, value);
}
static String temperatureUnitsConverter(Context context, String unit) {
switch (unit) {
case LocalePreferences.TemperatureUnit.CELSIUS:
return context.getString(R.string.celsius_temperature_unit);
case LocalePreferences.TemperatureUnit.FAHRENHEIT:
return context.getString(R.string.fahrenheit_temperature_unit);
default:
return context.getString(R.string.default_string_of_regional_preference);
}
}
static String dayConverter(Context context, String day) {
switch (day) {
case LocalePreferences.FirstDayOfWeek.MONDAY:
return context.getString(R.string.monday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.TUESDAY:
return context.getString(R.string.tuesday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.WEDNESDAY:
return context.getString(R.string.wednesday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.THURSDAY:
return context.getString(R.string.thursday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.FRIDAY:
return context.getString(R.string.friday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.SATURDAY:
return context.getString(R.string.saturday_first_day_of_week);
case LocalePreferences.FirstDayOfWeek.SUNDAY:
return context.getString(R.string.sunday_first_day_of_week);
default:
return context.getString(R.string.default_string_of_regional_preference);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/** Provides entries of each regional preferences */
@SearchIndexable
public class RegionalPreferencesEntriesFragment extends DashboardFragment {
private static final String TAG = RegionalPreferencesEntriesFragment.class.getSimpleName();
static final String ARG_KEY_REGIONAL_PREFERENCE = "arg_key_regional_preference";
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.regional_preferences_title);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.REGIONAL_PREFERENCE;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.regional_preference_main_page;
}
/**
* Get the tag string for logging.
*/
@Override
protected String getLogTag() {
return TAG;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.regional_preference_main_page);
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.content.Context;
import android.util.Log;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.widget.PreferenceCategoryController;
/** Category preference controller for temperature preferences. */
public class TemperatureUnitCategoryController extends PreferenceCategoryController {
private static final String LOG_TAG = "TemperatureUnitCategoryController";
private static final String KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT =
"temperature_unit_category";
private static final String KEY_PREFERENCE_TEMPERATURE_UNIT = "temperature_unit_list";
private PreferenceCategory mPreferenceCategory;
private TemperatureUnitListController mTemperatureUnitListController;
public TemperatureUnitCategoryController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceCategory = screen.findPreference(KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT);
if (mPreferenceCategory == null) {
Log.d(LOG_TAG, "displayPreference(), Can not find the category.");
return;
}
mPreferenceCategory.setVisible(isAvailable());
mTemperatureUnitListController = new TemperatureUnitListController(mContext,
KEY_PREFERENCE_TEMPERATURE_UNIT);
mTemperatureUnitListController.displayPreference(screen);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.settings.regionalpreferences;
import android.content.Context;
import android.provider.Settings;
import androidx.core.text.util.LocalePreferences;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import java.util.Locale;
/** A controller for the entry of Temperature units' page */
public class TemperatureUnitController extends BasePreferenceController {
private static final String TAG = TemperatureUnitController.class.getSimpleName();
public TemperatureUnitController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
/**
* @return {@link AvailabilityStatus} for the Setting. This status is used to determine if the
* Setting should be shown or disabled in Settings. Further, it can be used to produce
* appropriate error / warning Slice in the case of unavailability.
* </p>
* The status is used for the convenience methods: {@link #isAvailable()}, {@link
* #isSupported()}
* </p>
* The inherited class doesn't need to check work profile if android:forWork="true" is set in
* preference xml.
*/
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
String record = Settings.System.getString(
mContext.getContentResolver(), Settings.System.LOCALE_PREFERENCES);
String result = "";
if (record != null) {
result = LocalePreferences.getTemperatureUnit(Locale.forLanguageTag(record), false);
}
if (result.isEmpty()) {
result = LocalePreferences.getTemperatureUnit(false);
}
return result.isEmpty()
? mContext.getString(R.string.default_string_of_regional_preference)
: RegionalPreferencesDataUtils.temperatureUnitsConverter(mContext, result);
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/** Main fragment to display temperature preferences. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class TemperatureUnitFragment extends DashboardFragment {
private static final String LOG_TAG = "TemperatureUnitFragment";
private static final String KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT =
"temperature_unit_category";
@Override
protected int getPreferenceScreenResId() {
return R.xml.regional_preferences_temperature;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.TEMPERATURE_PREFERENCE;
}
@Override
protected String getLogTag() {
return LOG_TAG;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new TemperatureUnitCategoryController(context,
KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT));
return controllers;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.regional_preferences_temperature);
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (C) 2023 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.settings.regionalpreferences;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
/** A controller for handling all temperature preferences. */
public class TemperatureUnitListController extends RegionalPreferenceListBasePreferenceController {
private static final String KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT =
"temperature_unit_category";
private static final String KEY_PREFERENCE_TEMPERATURE_UNIT = "temperature_unit_list";
public TemperatureUnitListController(Context context, String key) {
super(context, key);
}
@Override
protected String getPreferenceTitle(String item) {
return RegionalPreferencesDataUtils.temperatureUnitsConverter(mContext, item);
}
@Override
protected String getPreferenceCategoryKey() {
return KEY_PREFERENCE_CATEGORY_TEMPERATURE_UNIT;
}
@Override
public String getPreferenceKey() {
return KEY_PREFERENCE_TEMPERATURE_UNIT;
}
@Override
protected String getExtensionTypes() {
return ExtensionTypes.TEMPERATURE_UNIT;
}
@Override
protected String[] getUnitValues() {
return mContext.getResources().getStringArray(R.array.temperature_units);
}
@Override
protected int getMetricsActionKey() {
return SettingsEnums.ACTION_SET_TEMPERATURE_UNIT;
}
}