improvements to Locale changing in app

This commit is contained in:
Nathan Freitas 2016-01-20 16:52:59 -05:00
parent 9af00fe263
commit 6eb0a93dd8
3 changed files with 121 additions and 75 deletions

View File

@ -1,10 +1,14 @@
package info.guardianproject.util; package info.guardianproject.util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@ -16,11 +20,9 @@ import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
public class Languages { public class Languages {
private static final String TAG = "Languages"; public static final String TAG = "Languages";
private static Languages singleton;
private static Map<String, String> tmpMap = new TreeMap<String, String>(); public static final Locale defaultLocale;
private static Map<String, String> nameMap;
public static final String USE_SYSTEM_DEFAULT = "";
public static final Locale TIBETAN = new Locale("bo"); public static final Locale TIBETAN = new Locale("bo");
static final Locale localesToTest[] = { static final Locale localesToTest[] = {
Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN, Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN,
@ -50,68 +52,142 @@ public class Languages {
new Locale("uz"), new Locale("vi"), new Locale("zu"), new Locale("uz"), new Locale("vi"), new Locale("zu"),
}; };
private Languages(Activity activity, int resId, String defaultString) { private static final String USE_SYSTEM_DEFAULT = "";
private static final String defaultString = "Use System Default";
private static Locale locale;
private static Languages singleton;
private static Class<?> clazz;
private static int resId;
private static Map<String, String> tmpMap = new TreeMap<String, String>();
private static Map<String, String> nameMap;
static {
defaultLocale = Locale.getDefault();
}
private Languages(Activity activity) {
AssetManager assets = activity.getAssets(); AssetManager assets = activity.getAssets();
Configuration config = activity.getResources().getConfiguration(); Configuration config = activity.getResources().getConfiguration();
DisplayMetrics metrics = new DisplayMetrics(); // Resources() requires DisplayMetrics, but they are only needed for drawables
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); DisplayMetrics ignored = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(ignored);
Resources resources; Resources resources;
Set<Locale> localeSet = new LinkedHashSet<Locale>(); Set<Locale> localeSet = new LinkedHashSet<Locale>();
for (Locale locale : localesToTest) { for (Locale locale : localesToTest) {
config.locale = locale; config.locale = locale;
resources = new Resources(assets, metrics, config); resources = new Resources(assets, ignored, config);
if (!TextUtils.equals(defaultString, resources.getString(resId)) if (!TextUtils.equals(defaultString, resources.getString(resId))
|| locale.equals(Locale.ENGLISH)) || locale.equals(Locale.ENGLISH))
localeSet.add(locale); localeSet.add(locale);
} }
for (Locale locale : localeSet) { for (Locale locale : localeSet) {
if (locale.equals(TIBETAN)) { if (locale.equals(TIBETAN)) {
// include English name for devices that don't support Tibetan // include English name for devices without Tibetan font support
// font
tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan
} else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) { } else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) {
tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese (China)
// (China)
} else if (locale.equals(Locale.TRADITIONAL_CHINESE)) { } else if (locale.equals(Locale.TRADITIONAL_CHINESE)) {
tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese (Taiwan)
// (Taiwan)
} else { } else {
tmpMap.put(locale.getLanguage(), locale.getDisplayLanguage(locale)); tmpMap.put(locale.getLanguage(), locale.getDisplayLanguage(locale));
} }
} }
// TODO implement this completely, the menu item works, but doesn't work
// properly
/* USE_SYSTEM_DEFAULT is a fake one for displaying in a chooser menu. */ /* USE_SYSTEM_DEFAULT is a fake one for displaying in a chooser menu. */
// localeSet.add(null); localeSet.add(null);
// tmpMap.put(USE_SYSTEM_DEFAULT, tmpMap.put(USE_SYSTEM_DEFAULT, activity.getString(resId));
// activity.getString(R.string.use_system_default));
nameMap = Collections.unmodifiableMap(tmpMap); nameMap = Collections.unmodifiableMap(tmpMap);
} }
/** /**
* Get the instance of {@link Languages} to work with, providing the * Get the instance of {@link Languages} to work with, providing the
* {@link Activity} that is will be working as part of. This uses the * {@link Activity} that is will be working as part of, as well as the
* provided string resource {@code resId} find the supported translations: * {@code resId} that has the exact string "Use System Default",
* if an included translation has a translated string that matches that * i.e. {@code R.string.use_system_default}.
* {@code resId}, i.e. {@code R.string.menu_settings}, then that language * <p/>
* will be included as a supported language. * That string resource {@code resId} is also used to find the supported
* * translations: if an included translation has a translated string that
* @param activity the {@link Activity} this is working as part of * matches that {@code resId}, then that language will be included as a
* @param resId the string resource ID to test, e.g. * supported language.
* {@code R.string.menu_settings} *
* @param defaultString the string resource in the default language, e.g. * @param clazz the {@link Class} of the default {@code Activity},
* {@code "Settings"} * usually the main {@code Activity} from where the
* Settings is launched from.
* @param resId the string resource ID to for the string "Use System Default",
* e.g. {@code R.string.use_system_default}
* @return * @return
*/ */
public static Languages get(Activity activity, int resId, String defaultString) { public static void setup(Class<?> clazz, int resId) {
if (singleton == null) if (Languages.clazz == null) {
singleton = new Languages(activity, resId, defaultString); Languages.clazz = clazz;
Languages.resId = resId;
} else {
throw new RuntimeException("Languages singleton was already initialized, duplicate call to Languages.setup()!");
}
}
/**
* Get the singleton to work with.
*
* @param activity the {@link Activity} this is working as part of
* @return
*/
public static Languages get(Activity activity) {
if (singleton == null) {
singleton = new Languages(activity);
}
return singleton; return singleton;
} }
//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@SuppressLint("NewApi")
public static void setLanguage(final ContextWrapper contextWrapper, String language, boolean refresh) {
if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) {
return; // already configured
} else if (language == null || language == USE_SYSTEM_DEFAULT) {
locale = defaultLocale;
} else {
/* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */
String localeSplit[] = language.split("_");
if (localeSplit.length > 1) {
locale = new Locale(localeSplit[0], localeSplit[1]);
} else {
locale = new Locale(language);
}
}
final Resources resources = contextWrapper.getBaseContext().getResources();
Configuration config = resources.getConfiguration();
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale);
} else {
config.locale = locale;
}
resources.updateConfiguration(config, resources.getDisplayMetrics());
Locale.setDefault(locale);
}
/**
* Force reload the {@link Activity to make language changes take effect.}
*
* @param activity the {@code Activity} to force reload
*/
public static void forceChangeLanguage(Activity activity) {
Intent intent = activity.getIntent();
if (intent == null) // when launched as LAUNCHER
return;
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
activity.finish();
activity.overridePendingTransition(0, 0);
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
}
/** /**
* Return the name of the language based on the locale. * Return the name of the language based on the locale.
* *
* @param locale * @param locale
* @return * @return
*/ */
@ -127,7 +203,7 @@ public class Languages {
/** /**
* Return an array of the names of all the supported languages, sorted to * Return an array of the names of all the supported languages, sorted to
* match what is returned by {@link Languages#getSupportedLocales()}. * match what is returned by {@link Languages#getSupportedLocales()}.
* *
* @return * @return
*/ */
public String[] getAllNames() { public String[] getAllNames() {
@ -147,7 +223,7 @@ public class Languages {
/** /**
* Get sorted list of supported locales. * Get sorted list of supported locales.
* *
* @return * @return
*/ */
public String[] getSupportedLocales() { public String[] getSupportedLocales() {

View File

@ -39,7 +39,8 @@ public class OrbotApp extends Application implements OrbotConstants
super.onCreate(); super.onCreate();
Prefs.setContext(this); Prefs.setContext(this);
setNewLocale(Prefs.getDefaultLocale()); Languages.setup(OrbotMainActivity.class, R.string.menu_settings);
Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
appBinHome = getDir(TorServiceConstants.DIRECTORY_TOR_BINARY,Application.MODE_PRIVATE); appBinHome = getDir(TorServiceConstants.DIRECTORY_TOR_BINARY,Application.MODE_PRIVATE);
appCacheHome = getDir(TorServiceConstants.DIRECTORY_TOR_DATA,Application.MODE_PRIVATE); appCacheHome = getDir(TorServiceConstants.DIRECTORY_TOR_DATA,Application.MODE_PRIVATE);
@ -60,40 +61,9 @@ public class OrbotApp extends Application implements OrbotConstants
public void onConfigurationChanged(Configuration newConfig) { public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
Log.i(TAG, "onConfigurationChanged " + newConfig.locale.getLanguage()); Log.i(TAG, "onConfigurationChanged " + newConfig.locale.getLanguage());
setNewLocale(Prefs.getDefaultLocale()); Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
} }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void setNewLocale(String language) {
if (TextUtils.isEmpty(language))
return;
if (locale != null && TextUtils.equals(locale.getLanguage(), language))
return; // already configured
/* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */
String localeSplit[] = language.split("_");
if (localeSplit.length > 1)
locale = new Locale(localeSplit[0], localeSplit[1]);
else
locale = new Locale(language);
Configuration config = getResources().getConfiguration();
if (Build.VERSION.SDK_INT >= 17)
config.setLocale(locale);
else
config.locale = locale;
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
/*
* Set the preference after setting the locale in case something goes
* wrong. If setting the locale causes an Exception, it should be set in
* the preferences, otherwise ChatSecure will be stuck in a crash loop.
*/
Prefs.setDefaultLocale(language);
Log.i(TAG, "setNewLocale complete: locale: " + locale.getLanguage()
+ " Locale.getDefault: " + Locale.getDefault().getLanguage());
}
public static void forceChangeLanguage(Activity activity) { public static void forceChangeLanguage(Activity activity) {
Intent intent = activity.getIntent(); Intent intent = activity.getIntent();
if (intent == null) // when launched as LAUNCHER if (intent == null) // when launched as LAUNCHER
@ -106,6 +76,6 @@ public class OrbotApp extends Application implements OrbotConstants
} }
public static Languages getLanguages(Activity activity) { public static Languages getLanguages(Activity activity) {
return Languages.get(activity, R.string.menu_settings, "Settings"); return Languages.get(activity);
} }
} }

View File

@ -56,7 +56,7 @@ public class SettingsPreferences
prefLocale = (ListPreference) findPreference("pref_default_locale"); prefLocale = (ListPreference) findPreference("pref_default_locale");
prefLocale.setOnPreferenceClickListener(this); prefLocale.setOnPreferenceClickListener(this);
Languages languages = Languages.get(this, R.string.menu_settings, "Settings"); Languages languages = Languages.get(this);
prefLocale.setEntries(languages.getAllNames()); prefLocale.setEntries(languages.getAllNames());
prefLocale.setEntryValues(languages.getSupportedLocales()); prefLocale.setEntryValues(languages.getSupportedLocales());
prefLocale.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { prefLocale.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@ -71,7 +71,7 @@ public class SettingsPreferences
String lang = settings.getString("pref_default_locale", String lang = settings.getString("pref_default_locale",
Locale.getDefault().getLanguage()); Locale.getDefault().getLanguage());
OrbotApp app = (OrbotApp) getApplication(); OrbotApp app = (OrbotApp) getApplication();
app.setNewLocale(language); Languages.setLanguage(app, language, true);
lang = settings.getString("pref_default_locale", lang = settings.getString("pref_default_locale",
Locale.getDefault().getLanguage()); Locale.getDefault().getLanguage());
OrbotApp.forceChangeLanguage(SettingsPreferences.this); OrbotApp.forceChangeLanguage(SettingsPreferences.this);