Skip to content

Commit

Permalink
Migrate to Android 13s per app locale support
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpoole committed Aug 3, 2023
1 parent 2e09a5f commit d844050
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 62 deletions.
4 changes: 2 additions & 2 deletions documentation/docs/help/en/Advanced preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ Select the theme to use. _Follow system_ will follow the setting in the system p

Show the menu buttons at the bottom of the screen. Default: _on_. You need to restart the app for changes to this setting to take effect.

### Disable translations
### App language

Use English for the user interface. Google does not provide a supported way to switch languages for individual apps, as a result this setting relies on multiple workarounds that may, or may not, work on your device. Preset translations can be disabled in the preset configurations.
Select a language for the user interface that is different from the device default. On devices running Android 13 and later the app language can be changed in the system settings too. Preset translations can be disabled in the preset configurations.

### Max. number of inline values

Expand Down
11 changes: 11 additions & 0 deletions src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,17 @@
android:configChanges="orientation|screenSize|keyboardHidden|density|screenLayout|uiMode|fontScale"
android:foregroundServiceType="location"
android:label="TrackerService" />

<!-- Service for backwards compatible per app locale support -->
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>


<provider
android:name="androidx.core.content.FileProvider"
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/de/blau/android/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@

import com.faendir.rhino_android.AndroidContextFactory;
import com.faendir.rhino_android.RhinoAndroidHelper;
import com.zeugmasolutions.localehelper.LocaleAwareApplication;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.IntentFilter;
import android.content.SharedPreferences;
Expand Down Expand Up @@ -72,7 +72,7 @@
@AcraHttpSender(httpMethod = HttpSender.Method.POST, uri = "https://acrarium.vespucci.io/")
@AcraDialog(resText = R.string.crash_dialog_text, resCommentPrompt = R.string.crash_dialog_comment_prompt, resTheme = R.style.Theme_AppCompat_Light_Dialog)

public class App extends LocaleAwareApplication implements android.app.Application.ActivityLifecycleCallbacks {
public class App extends Application implements android.app.Application.ActivityLifecycleCallbacks {
private static final String DEBUG_TAG = App.class.getCanonicalName();

private static final String RHINO_LAZY_LOAD = "lazyLoad";
Expand Down
68 changes: 45 additions & 23 deletions src/main/java/de/blau/android/prefs/AdvancedPrefEditorFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@
import java.util.List;
import java.util.Locale;

import com.zeugmasolutions.localehelper.LocaleAwareCompatActivity;

import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.preference.CheckBoxPreference;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.os.LocaleListCompat;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.Preference.OnPreferenceChangeListener;
import de.blau.android.R;
import de.blau.android.util.LocaleUtils;
import de.blau.android.util.Util;
Expand Down Expand Up @@ -63,6 +61,11 @@ public void onResume() {
setupCameraPref(cameraAppPref);
}

ListPreference appLocalePref = (ListPreference) getPreferenceScreen().findPreference(r.getString(R.string.config_appLocale_key));
if (appLocalePref != null) {
setupAppLocalePref(appLocalePref);
}

setListPreferenceSummary(R.string.config_selectCameraApp_key, false);
setListPreferenceSummary(R.string.config_theme_key, true);
setListPreferenceSummary(R.string.config_fullscreenMode_key, true);
Expand All @@ -87,6 +90,43 @@ public void onResume() {
setTitle();
}

/**
* Setup the app local preference
*
* @param appLocalePref the preference
*
*/
private void setupAppLocalePref(@NonNull ListPreference appLocalePref) {
Locale currentLocale = Locale.getDefault();
LocaleListCompat appLocales = LocaleUtils.getSupportedLocales(getContext());
LocaleListCompat currentLocales = AppCompatDelegate.getApplicationLocales();
if (!currentLocales.isEmpty()) {
LocaleListCompat temp = LocaleListCompat.getAdjustedDefault();
if (!temp.isEmpty()) {
currentLocale = temp.get(0);
}
}
String[] entries = new String[appLocales.size()];
String[] values = new String[appLocales.size()];
for (int i = 0; i < appLocales.size(); i++) {
Locale l = appLocales.get(i);
entries[i] = l.getDisplayName(currentLocale);
values[i] = l.toString();
}
appLocalePref.setEntryValues(values);
appLocalePref.setEntries(entries);
appLocalePref.setDefaultValue(currentLocale.toString());
OnPreferenceChangeListener p = (preference, newValue) -> {
Log.d(DEBUG_TAG, "onPreferenceChange appLocale " + newValue);
LocaleListCompat newDefaultList = LocaleListCompat.forLanguageTags((String) newValue);
Locale newDefault = newDefaultList.get(0);
AppCompatDelegate.setApplicationLocales(newDefaultList);
preference.setSummary(newDefault.getDisplayName(newDefault));
return true;
};
appLocalePref.setOnPreferenceChangeListener(p);
}

/**
* Setup the possible camera apps for selection
*
Expand Down Expand Up @@ -134,23 +174,5 @@ private void setOnPreferenceClickListeners() {
return true;
});
}

Preference disableTranslationsPref = getPreferenceScreen().findPreference(r.getString(R.string.config_disableTranslations_key));
if (disableTranslationsPref != null) {
disableTranslationsPref.setOnPreferenceClickListener(preference -> {
LocaleAwareCompatActivity lac = ((LocaleAwareCompatActivity) getActivity());
String savedLocaleKey = lac.getString(R.string.config_savedLocale_key);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(lac);
if (((CheckBoxPreference) disableTranslationsPref).isChecked()) {
if (!prefs.contains(savedLocaleKey)) {
prefs.edit().putString(savedLocaleKey, LocaleUtils.toLanguageTag(Locale.getDefault())).commit();
}
lac.updateLocale(Locale.ENGLISH);
} else {
lac.updateLocale(LocaleUtils.forLanguageTag(prefs.getString(savedLocaleKey, Locale.ENGLISH.toString())));
}
return true;
});
}
}
}
5 changes: 2 additions & 3 deletions src/main/java/de/blau/android/prefs/VespucciURLActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.zeugmasolutions.localehelper.LocaleAwareCompatActivity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
Expand All @@ -19,6 +17,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import de.blau.android.Authorize;
import de.blau.android.R;
import de.blau.android.net.OAuthHelper;
Expand All @@ -41,7 +40,7 @@
* @author Simon
*
*/
public class VespucciURLActivity extends LocaleAwareCompatActivity implements OnClickListener {
public class VespucciURLActivity extends AppCompatActivity implements OnClickListener {
private static final String DEBUG_TAG = VespucciURLActivity.class.getSimpleName();

private static final int REQUEST_PRESETEDIT = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

import org.acra.ACRA;

import com.zeugmasolutions.localehelper.LocaleAwareCompatActivity;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
Expand All @@ -17,6 +15,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
Expand Down Expand Up @@ -45,7 +44,7 @@
* @author simon
*/
public class PropertyEditorActivity<M extends Map<String, String> & Serializable, L extends List<PresetElementPath> & Serializable, T extends List<Map<String, String>> & Serializable>
extends LocaleAwareCompatActivity implements ControlListener {
extends AppCompatActivity implements ControlListener {

private static final String DEBUG_TAG = PropertyEditorActivity.class.getSimpleName();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package de.blau.android.util;

import com.zeugmasolutions.localehelper.LocaleAwareCompatActivity;

import android.content.res.Configuration;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import de.blau.android.App;

public abstract class ConfigurationChangeAwareActivity extends LocaleAwareCompatActivity {
public abstract class ConfigurationChangeAwareActivity extends AppCompatActivity {

private static final String DEBUG_TAG = ConfigurationChangeAwareActivity.class.getSimpleName();

@Override
Expand All @@ -20,6 +19,6 @@ public void onConfigurationChanged(Configuration newConfig) {
Log.d(DEBUG_TAG, "recreating activity " + this.getClass().getCanonicalName());
recreate();
}
Util.clearCaches(this, oldConfig, newConfig);
Util.clearCaches(this, oldConfig, newConfig);
}
}
41 changes: 41 additions & 0 deletions src/main/java/de/blau/android/util/LocaleUtils.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package de.blau.android.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.LocaleConfig;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.os.LocaleListCompat;
import de.blau.android.R;

public final class LocaleUtils {

private static final String DEBUG_TAG = LocaleUtils.class.getSimpleName();

// list of languages that use Latin script from https://gist.github.com/phil-brown/8056700
private static Set<String> latin = new HashSet<>(Arrays.asList("aa", "ace", "ach", "ada", "af", "agq", "ak", "ale", "amo", "an", "arn", "arp", "arw", "asa",
"ast", "ay", "az", "bal", "ban", "bas", "bbc", "bem", "bez", "bi", "bik", "bin", "bku", "bla", "bm", "bqv", "br", "bs", "buc", "bug", "bya", "ca",
Expand Down Expand Up @@ -171,4 +184,32 @@ public static Locale forLanguageTag(@NonNull String languageTag) {
public static boolean usesLatinScript(@NonNull Locale locale) {
return latin.contains(locale.getLanguage());
}

/**
* Get a list of supported locales for the app
*
* For devices prior to Android 13 this reads and parses locales_config.xml directly, note that since we are using
* automatic generation of the file it has a different name.
*
* @param context an Android Context
* @return a LocaleListCompat
*/
public static LocaleListCompat getSupportedLocales(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return LocaleListCompat.wrap(new LocaleConfig(context).getSupportedLocales());
}
List<String> locales = new ArrayList<>();
try {
XmlPullParser parser = context.getResources().getXml(R.xml._generated_res_locale_config);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() == XmlPullParser.START_TAG && "locale".equals(parser.getName())) {
locales.add(parser.getAttributeValue(0));
}
parser.next();
}
} catch (XmlPullParserException | IOException e) {
Log.e(DEBUG_TAG, "Error reading locales_config " + e.getMessage());
}
return LocaleListCompat.forLanguageTags(String.join(",", locales));
}
}
1 change: 1 addition & 0 deletions src/main/res/resources.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
3 changes: 1 addition & 2 deletions src/main/res/values/prefkeys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@
<string name="config_useBarometricHeight_key">useBarometricHeight</string>
<string name="config_useUrlForFeedback_key">useUrlForFeedback</string>
<string name="config_preferRemovableStorage_key">preferRemovableStorage</string>
<string name="config_disableTranslations_key">disableTranslations</string>
<string name="config_savedLocale_key">savedLocale</string>
<string name="config_appLocale_key">appLocale</string>
<string name="config_nameCap_key">nameCap</string>
<string name="config_indexMediaStore_key">indexMediaStore</string>
<string name="config_wayNodeDragging_key">wayNodeDragging</string>
Expand Down
4 changes: 2 additions & 2 deletions src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1174,8 +1174,8 @@
<string name="config_theme_summary">Select app styling.</string>
<string name="config_splitActionBarEnabled_title">Enable split action bar</string>
<string name="config_splitActionBarEnabled_summary">Show the menu buttons at the bottom of the screen. Requires starting Vespucci again, and Android 4.0 or later.</string>
<string name="config_disableTranslations_title">Disable translations</string>
<string name="config_disableTranslations_summary">Use English for the user interface</string>
<string name="config_appLocale_title">App language</string>
<string name="config_appLocale_summary">Select non-standard app language</string>
<string name="config_maxInlineValues_title">Max. number of inline values</string>
<string name="config_maxInlineValues_summary">Maximum number of inline values displayed in tag form.</string>
<string name="config_maxInlineValues_current">%1$d values</string>
Expand Down
10 changes: 5 additions & 5 deletions src/main/res/xml-v19/advancedpreferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@
android:key="@string/config_splitActionBarEnabled_key"
android:summary="@string/config_splitActionBarEnabled_summary"
android:title="@string/config_splitActionBarEnabled_title" />
<androidx.preference.CheckBoxPreference
android:defaultValue="false"
android:key="@string/config_disableTranslations_key"
android:summary="@string/config_disableTranslations_summary"
android:title="@string/config_disableTranslations_title" />
<androidx.preference.ListPreference
android:key="@string/config_appLocale_key"
android:title="@string/config_appLocale_title"
android:summary="@string/config_appLocale_summary"
android:defaultValue="en" />
<ch.poole.android.numberpickerpreference.NumberPickerPreference
android:defaultValue="4"
android:key="@string/config_maxInlineValues_key"
Expand Down
10 changes: 5 additions & 5 deletions src/main/res/xml-v24/advancedpreferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@
android:key="@string/config_splitActionBarEnabled_key"
android:summary="@string/config_splitActionBarEnabled_summary"
android:title="@string/config_splitActionBarEnabled_title" />
<androidx.preference.CheckBoxPreference
android:defaultValue="false"
android:key="@string/config_disableTranslations_key"
android:summary="@string/config_disableTranslations_summary"
android:title="@string/config_disableTranslations_title" />
<androidx.preference.ListPreference
android:key="@string/config_appLocale_key"
android:title="@string/config_appLocale_title"
android:summary="@string/config_appLocale_summary"
android:defaultValue="en" />
<ch.poole.android.numberpickerpreference.NumberPickerPreference
android:defaultValue="4"
android:key="@string/config_maxInlineValues_key"
Expand Down
10 changes: 5 additions & 5 deletions src/main/res/xml-v29/advancedpreferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@
android:key="@string/config_splitActionBarEnabled_key"
android:summary="@string/config_splitActionBarEnabled_summary"
android:title="@string/config_splitActionBarEnabled_title" />
<androidx.preference.CheckBoxPreference
android:defaultValue="false"
android:key="@string/config_disableTranslations_key"
android:summary="@string/config_disableTranslations_summary"
android:title="@string/config_disableTranslations_title" />
<androidx.preference.ListPreference
android:key="@string/config_appLocale_key"
android:title="@string/config_appLocale_title"
android:summary="@string/config_appLocale_summary"
android:defaultValue="en" />
<ch.poole.android.numberpickerpreference.NumberPickerPreference
android:defaultValue="4"
android:key="@string/config_maxInlineValues_key"
Expand Down
10 changes: 5 additions & 5 deletions src/main/res/xml/advancedpreferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@
android:key="@string/config_splitActionBarEnabled_key"
android:summary="@string/config_splitActionBarEnabled_summary"
android:title="@string/config_splitActionBarEnabled_title" />
<androidx.preference.CheckBoxPreference
android:defaultValue="false"
android:key="@string/config_disableTranslations_key"
android:summary="@string/config_disableTranslations_summary"
android:title="@string/config_disableTranslations_title" />
<androidx.preference.ListPreference
android:key="@string/config_appLocale_key"
android:title="@string/config_appLocale_title"
android:summary="@string/config_appLocale_summary"
android:defaultValue="en" />
<ch.poole.android.numberpickerpreference.NumberPickerPreference
android:defaultValue="4"
android:key="@string/config_maxInlineValues_key"
Expand Down

0 comments on commit d844050

Please sign in to comment.