From 4262e60577ac0a5e9017f3f8891a4d59e96ea265 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 30 Nov 2024 09:29:30 +0100 Subject: [PATCH] Settings: add custom language/locale chooser (SDK33+). Corsican was not available using Android's per-app-language-chooser. Fixes #1974. --- .../opentracks/settings/LocalePreference.java | 107 ++++++++++++++++++ src/main/res/values/settings.xml | 3 + src/main/res/values/strings.xml | 3 + src/main/res/xml/settings_user_interface.xml | 6 + 4 files changed, 119 insertions(+) create mode 100644 src/main/java/de/dennisguse/opentracks/settings/LocalePreference.java diff --git a/src/main/java/de/dennisguse/opentracks/settings/LocalePreference.java b/src/main/java/de/dennisguse/opentracks/settings/LocalePreference.java new file mode 100644 index 000000000..db603fad5 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/settings/LocalePreference.java @@ -0,0 +1,107 @@ +package de.dennisguse.opentracks.settings; + +import android.app.LocaleConfig; +import android.content.Context; +import android.os.Build; +import android.os.LocaleList; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.os.LocaleListCompat; +import androidx.preference.ListPreference; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Locale; + +import de.dennisguse.opentracks.R; + +public class LocalePreference extends ListPreference { + + public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context); + } + + public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public LocalePreference(@NonNull Context context) { + super(context); + init(context); + } + + private void init(Context context) { + setPersistent(false); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + setEnabled(false); + return; + } + LocaleItem systemDefaultLocale = new LocaleItem("", context.getString(R.string.settings_locale_system_default)); + + LocaleItem currentLocale = new LocaleItem(Locale.getDefault().toLanguageTag(), Locale.getDefault().getDisplayName()); + + // All available options + LocaleList supportedLocales = LocaleConfig.fromContextIgnoringOverride(context).getSupportedLocales(); + ArrayList localeItemsSorting = new ArrayList<>(); + for (int i = 0; i < supportedLocales.size(); i++) { + Locale current = supportedLocales.get(i); + localeItemsSorting.add(new LocaleItem(current.toLanguageTag(), current.getDisplayName())); + } + + localeItemsSorting.removeIf(current -> current.languageTag.equals(currentLocale.languageTag)); + localeItemsSorting.sort(Comparator.comparing(o -> o.displayName)); + + ArrayList localeItemList = new ArrayList<>(); + localeItemList.add(systemDefaultLocale); + localeItemList.add(currentLocale); + localeItemList.addAll(localeItemsSorting); + + ArrayList entries = new ArrayList<>(); + ArrayList entryValues = new ArrayList<>(); + + for (LocaleItem current : localeItemList) { + entries.add(current.displayName); + entryValues.add(current.languageTag); + } + + setEntries(entries.toArray(new String[]{})); + setEntryValues(entryValues.toArray(new String[]{})); + + if (AppCompatDelegate.getApplicationLocales().equals(LocaleListCompat.getEmptyLocaleList())) { + setValue(systemDefaultLocale.languageTag); + } else { + setValue(currentLocale.languageTag); + } + } + + @Override + public void setOnPreferenceChangeListener(@Nullable OnPreferenceChangeListener onPreferenceChangeListener) { + super.setOnPreferenceChangeListener(onPreferenceChangeListener); + } + + @Override + public boolean callChangeListener(Object newValue) { + LocaleListCompat newLocale = LocaleListCompat.getEmptyLocaleList(); + if (!newValue.equals("")) { + newLocale = LocaleListCompat.forLanguageTags((String) newValue); + } + AppCompatDelegate.setApplicationLocales(newLocale); + return super.callChangeListener(newValue); + } + + record LocaleItem( + String languageTag, + String displayName + ) { + } +} diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index 664ceac2f..1272fb9b0 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -278,6 +278,9 @@ uiDynamicColors false + + localeKey + nightMode @string/night_mode_system_value diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 75ebcee2d..f75a64ef1 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -371,6 +371,9 @@ limitations under the License. Number Default track name Idle threshold + Language + System default + UI Theme System Day diff --git a/src/main/res/xml/settings_user_interface.xml b/src/main/res/xml/settings_user_interface.xml index 78d6abeaa..e1eb4fa90 100644 --- a/src/main/res/xml/settings_user_interface.xml +++ b/src/main/res/xml/settings_user_interface.xml @@ -4,6 +4,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:title="@string/settings_ui_title"> + +