From 9436f9ec558ed1c66d28ea048bab2071543e22d0 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Thu, 15 Aug 2024 15:58:19 +0200 Subject: [PATCH] Add support for Android 33+ per-app language On Android 33+, PCAPdroid now opens the system per-app language selector. On previous versions, the in-app selector is used. Closes #456 --- app/build.gradle | 4 ++ .../com/emanuelef/remote_capture/Utils.java | 17 +++++- .../activities/prefs/SettingsActivity.java | 61 ++++++++++++++----- app/src/main/res/resources.properties | 1 + app/src/main/res/xml/root_preferences.xml | 7 +++ 5 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/resources.properties diff --git a/app/build.gradle b/app/build.gradle index 95808e8f..1ac604b8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,6 +44,10 @@ android { } } + androidResources { + generateLocaleConfig true + } + externalNativeBuild { cmake { path file('src/main/jni/CMakeLists.txt') diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 7605a649..6e049eaa 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -22,6 +22,7 @@ package com.emanuelef.remote_capture; import android.Manifest; import android.annotation.SuppressLint; import android.app.ActivityManager; +import android.app.LocaleManager; import android.app.Notification; import android.app.PendingIntent; import android.app.UiModeManager; @@ -58,6 +59,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; +import android.os.LocaleList; import android.os.Looper; import android.provider.DocumentsContract; import android.provider.MediaStore; @@ -142,8 +144,6 @@ import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import javax.net.ssl.HttpsURLConnection; @@ -344,6 +344,19 @@ public class Utils { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); Configuration config = context.getResources().getConfiguration(); + // On Android 33+, app language is configured from the system settings + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (Prefs.useEnglishLanguage(prefs)) { + Log.i(TAG, "Migrate from in-app language picker to system picker"); + prefs.edit().remove(Prefs.PREF_APP_LANGUAGE).apply(); + + context.getSystemService(LocaleManager.class) + .setApplicationLocales(new LocaleList(Locale.forLanguageTag("en-US"))); + } + + return config; + } + if(!Prefs.useEnglishLanguage(prefs)) return config; diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java index 4d396d46..7be3f902 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/prefs/SettingsActivity.java @@ -19,10 +19,15 @@ package com.emanuelef.remote_capture.activities.prefs; +import android.app.LocaleManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.LocaleList; +import android.provider.Settings; import android.text.InputType; import androidx.activity.result.ActivityResultLauncher; @@ -353,28 +358,54 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment mSocks5Settings.setVisible(!tlsDecryption && !rootEnabled); } - private void setupOtherPrefs() { + private void setupAppLanguagePref() { DropDownPreference appLang = requirePreference(Prefs.PREF_APP_LANGUAGE); + Preference appLangExternal = requirePreference("app_language_external"); - if(SettingsActivity.ACTION_LANG_RESTART.equals(requireActivity().getIntent().getAction())) - scrollToPreference(appLang); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // On Android 33+, app language is configurable from the system settings + appLang.setVisible(false); + appLangExternal.setVisible(true); - // Current locale applied via BaseActivity.attachBaseContext - appLang.setOnPreferenceChangeListener((preference, newValue) -> { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); + LocaleList locales = requireContext().getSystemService(LocaleManager.class) + .getApplicationLocales(); + if (locales.equals(LocaleList.getEmptyLocaleList())) + appLangExternal.setSummary(getString(R.string.system_default)); + else if (!locales.isEmpty()) + appLangExternal.setSummary(locales.get(0).getDisplayName()); - if(prefs.edit().putString(Prefs.PREF_APP_LANGUAGE, newValue.toString()).commit()) { - // Restart the activity to apply the language change - Intent intent = new Intent(getContext(), SettingsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.setAction(SettingsActivity.ACTION_LANG_RESTART); + appLangExternal.setOnPreferenceClickListener(preference -> { + Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS); + intent.setData(Uri.fromParts("package", requireContext().getPackageName(), null)); startActivity(intent); + return true; + }); + } else { + // Fallback selector for older Android versions + if (SettingsActivity.ACTION_LANG_RESTART.equals(requireActivity().getIntent().getAction())) + scrollToPreference(appLang); - Runtime.getRuntime().exit(0); - } + // Current locale applied via BaseActivity.attachBaseContext + appLang.setOnPreferenceChangeListener((preference, newValue) -> { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); - return false; - }); + if (prefs.edit().putString(Prefs.PREF_APP_LANGUAGE, newValue.toString()).commit()) { + // Restart the activity to apply the language change + Intent intent = new Intent(requireContext(), SettingsActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.setAction(SettingsActivity.ACTION_LANG_RESTART); + startActivity(intent); + + Runtime.getRuntime().exit(0); + } + + return false; + }); + } + } + + private void setupOtherPrefs() { + setupAppLanguagePref(); DropDownPreference appTheme = requirePreference(Prefs.PREF_APP_THEME); appTheme.setOnPreferenceChangeListener((preference, newValue) -> { diff --git a/app/src/main/res/resources.properties b/app/src/main/res/resources.properties new file mode 100644 index 00000000..467b3efe --- /dev/null +++ b/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=en-US diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 37b63514..07d7ec4e 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -212,6 +212,13 @@ app:defaultValue="system" app:useSimpleSummaryProvider="true"/> + +