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
This commit is contained in:
emanuele-f 2024-08-15 15:58:19 +02:00
parent 80034e4711
commit 9436f9ec55
5 changed files with 73 additions and 17 deletions

View File

@ -44,6 +44,10 @@ android {
}
}
androidResources {
generateLocaleConfig true
}
externalNativeBuild {
cmake {
path file('src/main/jni/CMakeLists.txt')

View File

@ -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;

View File

@ -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) -> {

View File

@ -0,0 +1 @@
unqualifiedResLocale=en-US

View File

@ -212,6 +212,13 @@
app:defaultValue="system"
app:useSimpleSummaryProvider="true"/>
<Preference
app:key="app_language_external"
app:title="@string/app_language"
app:isPreferenceVisible="false"
app:useSimpleSummaryProvider="true"
app:iconSpaceReserved="false" />
<Preference
app:key="port_mapping"
app:title="@string/port_mapping"