diff --git a/res/values/strings.xml b/res/values/strings.xml index 54b6fbc0da2..79db5c87410 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -381,6 +381,18 @@ Next + + + Region + + Choose a region + + The region you choose affects how your device displays time, dates, temperature, and more. + + Suggested + + All regions + Languages diff --git a/res/xml/language_and_region_settings.xml b/res/xml/language_and_region_settings.xml index 5626f2206e8..d631748fab8 100644 --- a/res/xml/language_and_region_settings.xml +++ b/res/xml/language_and_region_settings.xml @@ -51,6 +51,13 @@ android:title="@string/regional_preferences_category_title" settings:controller="com.android.settings.regionalpreferences.RegionalPreferencesCategoryController"> + + + + + + + + + + + + + + diff --git a/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java b/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java new file mode 100644 index 00000000000..6c2c8b63331 --- /dev/null +++ b/src/com/android/settings/regionalpreferences/RegionPickerBaseListPreferenceController.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2024 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 android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceScreen; + +import com.android.internal.annotations.Initializer; +import com.android.internal.app.LocaleCollectorBase; +import com.android.internal.app.LocaleHelper; +import com.android.internal.app.LocalePicker; +import com.android.internal.app.LocaleStore; +import com.android.internal.app.LocaleStore.LocaleInfo; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class RegionPickerBaseListPreferenceController extends BasePreferenceController { + + private static final String TAG = "RegionPickerBaseListPreferenceController"; + private static final String KEY_SUGGESTED = "suggested"; + private PreferenceCategory mPreferenceCategory; + private Set mLocaleList; + private ArrayList mLocaleOptions; + + public RegionPickerBaseListPreferenceController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + mLocaleList = getLocaleCollectorController(context).getSupportedLocaleList(null, + false, false); + mLocaleOptions = new ArrayList<>(); + mLocaleOptions.ensureCapacity(mLocaleList.size()); + } + + @Override + @Initializer + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceCategory = screen.findPreference(getPreferenceCategoryKey()); + updatePreferences(); + } + + private void updatePreferences() { + if (mPreferenceCategory == null) { + Log.d(TAG, "updatePreferences, mPreferenceCategory is null"); + return; + } + List result; + LocaleStore.LocaleInfo parentLocale = getParentLocale(); + if (parentLocale != null) { + mLocaleList = getLocaleCollectorController(mContext).getSupportedLocaleList( + parentLocale, false, true); + } + mLocaleOptions.clear(); + mLocaleOptions.ensureCapacity(mLocaleList.size()); + result = getPreferenceCategoryKey().contains(KEY_SUGGESTED) + ? getSuggestedLocaleList() + : getSupportedLocaleList(); + if (getPreferenceCategoryKey().contains(KEY_SUGGESTED)) { + Locale systemLocale = Locale.getDefault(); + LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(systemLocale); + result.add(localeInfo); + } + result = getSortedLocaleList(result); + setupPreference(result); + } + + private void setupPreference(List localeInfoList) { + localeInfoList.stream().forEach(locale -> { + SelectorWithWidgetPreference pref = new SelectorWithWidgetPreference(mContext); + mPreferenceCategory.addPreference(pref); + pref.setTitle(locale.getFullCountryNameNative()); + pref.setKey(locale.toString()); + if (locale.getLocale().equals(Locale.getDefault())) { + pref.setChecked(true); + } else { + pref.setChecked(false); + } + pref.setOnClickListener(v -> { + switchRegion(locale); + }); + }); + mPreferenceCategory.setVisible(mPreferenceCategory.getPreferenceCount() > 0); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + protected abstract String getPreferenceCategoryKey(); + + protected abstract LocaleCollectorBase getLocaleCollectorController(Context context); + + @Nullable protected abstract LocaleStore.LocaleInfo getParentLocale(); + + protected List getSuggestedLocaleList() { + if (mLocaleList != null && !mLocaleList.isEmpty()) { + mLocaleOptions.addAll(mLocaleList.stream() + .filter(localeInfo -> localeInfo.isSuggested()) + .collect(Collectors.toList())); + } else { + Log.d(TAG, "Can not get suggested locales because the locale list is null or empty."); + } + + return mLocaleOptions; + } + + protected List getSupportedLocaleList() { + if (mLocaleList != null && !mLocaleList.isEmpty()) { + mLocaleOptions.addAll(mLocaleList.stream() + .filter(localeInfo -> !localeInfo.isSuggested()) + .collect(Collectors.toList())); + } else { + Log.d(TAG, "Can not get supported locales because the locale list is null or empty."); + } + return mLocaleOptions; + } + + private List getSortedLocaleList( + List localeInfos) { + final Locale sortingLocale = Locale.getDefault(); + final LocaleHelper.LocaleInfoComparator comp = + new LocaleHelper.LocaleInfoComparator(sortingLocale, true); + Collections.sort(localeInfos, comp); + return localeInfos; + } + + private void switchRegion(LocaleStore.LocaleInfo localeInfo) { + if (localeInfo.getLocale().equals(Locale.getDefault())) { + return; + } + updateRegion(localeInfo.getLocale().toLanguageTag()); + updatePreferences(); + } + + private void updateRegion(String selectedLanguageTag) { + LocaleList localeList = LocaleList.getDefault(); + Locale systemLocale = Locale.getDefault(); + Set extensionKeys = systemLocale.getExtensionKeys(); + Locale selectedLocale = Locale.forLanguageTag(selectedLanguageTag); + Locale.Builder builder = new Locale.Builder(); + builder.setLocale(selectedLocale); + if (!extensionKeys.isEmpty()) { + for (Character extKey : extensionKeys) { + builder.setExtension(extKey, systemLocale.getExtension(extKey)); + } + } + Locale newLocale = builder.build(); + Locale[] resultLocales = new Locale[localeList.size()]; + resultLocales[0] = newLocale; + for (int i = 1; i < localeList.size(); i++) { + resultLocales[i] = localeList.get(i); + } + LocalePicker.updateLocales(new LocaleList(resultLocales)); + } +} diff --git a/src/com/android/settings/regionalpreferences/RegionPickerFragment.java b/src/com/android/settings/regionalpreferences/RegionPickerFragment.java new file mode 100644 index 00000000000..b675d2a2551 --- /dev/null +++ b/src/com/android/settings/regionalpreferences/RegionPickerFragment.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 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.Bundle; + +import androidx.annotation.NonNull; + +import com.android.internal.app.LocaleStore; +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.flags.Flags; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class RegionPickerFragment extends DashboardFragment{ + + private static final String TAG = "RegionPickerFragment"; + private static final String KEY_PREFERENCE_SYSTEM_REGION_LIST = "system_region_list"; + private static final String KEY_PREFERENCE_SYSTEM_REGION_SUGGESTED_LIST = + "system_region_suggested_list"; + + @Override + public void onCreate(@NonNull Bundle icicle) { + super.onCreate(icicle); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.system_region_picker; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public int getMetricsCategory() { + return 0; + } + + @Override + protected List createPreferenceControllers(Context context) { + return buildPreferenceControllers(context); + } + + private List buildPreferenceControllers( + @NonNull Context context) { + Locale parentLocale = LocaleStore.getLocaleInfo(Locale.getDefault()).getParent(); + LocaleStore.LocaleInfo parentLocaleInfo = LocaleStore.getLocaleInfo(parentLocale); + SystemRegionSuggestedListPreferenceController mSuggestedListPreferenceController = + new SystemRegionSuggestedListPreferenceController( + context, KEY_PREFERENCE_SYSTEM_REGION_SUGGESTED_LIST, parentLocaleInfo); + SystemRegionAllListPreferenceController mSystemRegionAllListPreferenceController = + new SystemRegionAllListPreferenceController( + context, KEY_PREFERENCE_SYSTEM_REGION_LIST, parentLocaleInfo); + final List controllers = new ArrayList<>(); + controllers.add(mSuggestedListPreferenceController); + controllers.add(mSystemRegionAllListPreferenceController); + return controllers; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.system_region_picker) { + @Override + protected boolean isPageSearchEnabled(Context context) { + if (!Flags.regionalPreferencesApiEnabled()) { + return false; + } + return true; + } + }; +} diff --git a/src/com/android/settings/regionalpreferences/RegionPreferenceController.java b/src/com/android/settings/regionalpreferences/RegionPreferenceController.java new file mode 100644 index 00000000000..a9d87f0f181 --- /dev/null +++ b/src/com/android/settings/regionalpreferences/RegionPreferenceController.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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 androidx.annotation.NonNull; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.app.LocaleStore; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.flags.Flags; + +import java.util.Locale; + +/** A controller for the entry of region picker page */ +public class RegionPreferenceController extends BasePreferenceController { + + public RegionPreferenceController(@NonNull Context context, @NonNull String key) { + super(context, key); + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + Preference preference = screen.findPreference(getPreferenceKey()); + LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(Locale.getDefault()); + preference.setSummary(localeInfo.getFullCountryNameNative()); + } + + @Override + public int getAvailabilityStatus() { + if (!Flags.regionalPreferencesApiEnabled()) { + return CONDITIONALLY_UNAVAILABLE; + } + return AVAILABLE; + } +} diff --git a/src/com/android/settings/regionalpreferences/SystemRegionAllListPreferenceController.java b/src/com/android/settings/regionalpreferences/SystemRegionAllListPreferenceController.java new file mode 100644 index 00000000000..47cd248b00e --- /dev/null +++ b/src/com/android/settings/regionalpreferences/SystemRegionAllListPreferenceController.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.app.LocaleCollectorBase; +import com.android.internal.app.LocaleStore; +import com.android.internal.app.LocaleStore.LocaleInfo; +import com.android.internal.app.SystemLocaleCollector; + +public class SystemRegionAllListPreferenceController extends + RegionPickerBaseListPreferenceController { + + private static final String KEY_PREFERENCE_CATEGORY = "system_region_all_supported_category"; + private static final String KEY_PREFERENCE_SYSTEM_REGION_LIST = + "system_region_list"; + @Nullable private LocaleStore.LocaleInfo mLocaleInfo; + + public SystemRegionAllListPreferenceController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + } + + public SystemRegionAllListPreferenceController(@NonNull Context context, + @NonNull String preferenceKey, @NonNull LocaleStore.LocaleInfo parentLocale) { + super(context, preferenceKey); + mLocaleInfo = parentLocale; + } + + @Override + protected String getPreferenceCategoryKey() { + return KEY_PREFERENCE_CATEGORY; + } + + @Override + @NonNull + public String getPreferenceKey() { + return KEY_PREFERENCE_SYSTEM_REGION_LIST; + } + + @Override + protected LocaleCollectorBase getLocaleCollectorController(Context context) { + return new SystemLocaleCollector(context, null); + } + + @Nullable + @Override + protected LocaleInfo getParentLocale() { + return mLocaleInfo; + } +} diff --git a/src/com/android/settings/regionalpreferences/SystemRegionSuggestedListPreferenceController.java b/src/com/android/settings/regionalpreferences/SystemRegionSuggestedListPreferenceController.java new file mode 100644 index 00000000000..9654f2a26ea --- /dev/null +++ b/src/com/android/settings/regionalpreferences/SystemRegionSuggestedListPreferenceController.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.app.LocaleCollectorBase; +import com.android.internal.app.LocaleStore; +import com.android.internal.app.LocaleStore.LocaleInfo; +import com.android.internal.app.SystemLocaleCollector; + +public class SystemRegionSuggestedListPreferenceController extends + RegionPickerBaseListPreferenceController { + + private static final String KEY_PREFERENCE_CATEGORY = "system_region_suggested_category"; + private static final String KEY_PREFERENCE_SYSTEM_REGION_SUGGESTED_LIST = + "system_region_suggested_list"; + @Nullable private LocaleStore.LocaleInfo mLocaleInfo; + + public SystemRegionSuggestedListPreferenceController(@NonNull Context context, + @NonNull String preferenceKey) { + super(context, preferenceKey); + } + + public SystemRegionSuggestedListPreferenceController(@NonNull Context context, + @NonNull String preferenceKey, @NonNull LocaleStore.LocaleInfo parentLocale) { + super(context, preferenceKey); + mLocaleInfo = parentLocale; + } + + @Override + protected String getPreferenceCategoryKey() { + return KEY_PREFERENCE_CATEGORY; + } + + @Override + @NonNull + public String getPreferenceKey() { + return KEY_PREFERENCE_SYSTEM_REGION_SUGGESTED_LIST; + } + + @Override + protected LocaleCollectorBase getLocaleCollectorController(Context context) { + return new SystemLocaleCollector(context, null); + } + + @Nullable + @Override + protected LocaleInfo getParentLocale() { + return mLocaleInfo; + } +}