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;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.text.TextUtils;
import android.util.DisplayMetrics;
@ -16,11 +20,9 @@ import java.util.Set;
import java.util.TreeMap;
public class Languages {
private static final String TAG = "Languages";
private static Languages singleton;
private static Map<String, String> tmpMap = new TreeMap<String, String>();
private static Map<String, String> nameMap;
public static final String USE_SYSTEM_DEFAULT = "";
public static final String TAG = "Languages";
public static final Locale defaultLocale;
public static final Locale TIBETAN = new Locale("bo");
static final Locale localesToTest[] = {
Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN,
@ -50,68 +52,142 @@ public class Languages {
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();
Configuration config = activity.getResources().getConfiguration();
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
// Resources() requires DisplayMetrics, but they are only needed for drawables
DisplayMetrics ignored = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(ignored);
Resources resources;
Set<Locale> localeSet = new LinkedHashSet<Locale>();
for (Locale locale : localesToTest) {
config.locale = locale;
resources = new Resources(assets, metrics, config);
resources = new Resources(assets, ignored, config);
if (!TextUtils.equals(defaultString, resources.getString(resId))
|| locale.equals(Locale.ENGLISH))
localeSet.add(locale);
}
for (Locale locale : localeSet) {
if (locale.equals(TIBETAN)) {
// include English name for devices that don't support Tibetan
// font
// include English name for devices without Tibetan font support
tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan
} else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) {
tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese
// (China)
tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese (China)
} else if (locale.equals(Locale.TRADITIONAL_CHINESE)) {
tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese
// (Taiwan)
tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese (Taiwan)
} else {
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. */
// localeSet.add(null);
// tmpMap.put(USE_SYSTEM_DEFAULT,
// activity.getString(R.string.use_system_default));
localeSet.add(null);
tmpMap.put(USE_SYSTEM_DEFAULT, activity.getString(resId));
nameMap = Collections.unmodifiableMap(tmpMap);
}
/**
* Get the instance of {@link Languages} to work with, providing the
* {@link Activity} that is will be working as part of. This uses the
* provided string resource {@code resId} find the supported translations:
* if an included translation has a translated string that matches that
* {@code resId}, i.e. {@code R.string.menu_settings}, then that language
* will be included as a supported language.
*
* @param activity the {@link Activity} this is working as part of
* @param resId the string resource ID to test, e.g.
* {@code R.string.menu_settings}
* @param defaultString the string resource in the default language, e.g.
* {@code "Settings"}
* {@link Activity} that is will be working as part of, as well as the
* {@code resId} that has the exact string "Use System Default",
* i.e. {@code R.string.use_system_default}.
* <p/>
* That string resource {@code resId} is also used to find the supported
* translations: if an included translation has a translated string that
* matches that {@code resId}, then that language will be included as a
* supported language.
*
* @param clazz the {@link Class} of the default {@code Activity},
* 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
*/
public static Languages get(Activity activity, int resId, String defaultString) {
if (singleton == null)
singleton = new Languages(activity, resId, defaultString);
public static void setup(Class<?> clazz, int resId) {
if (Languages.clazz == null) {
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;
}
//@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.
*
*
* @param locale
* @return
*/
@ -127,7 +203,7 @@ public class Languages {
/**
* Return an array of the names of all the supported languages, sorted to
* match what is returned by {@link Languages#getSupportedLocales()}.
*
*
* @return
*/
public String[] getAllNames() {
@ -147,7 +223,7 @@ public class Languages {
/**
* Get sorted list of supported locales.
*
*
* @return
*/
public String[] getSupportedLocales() {

View File

@ -39,7 +39,8 @@ public class OrbotApp extends Application implements OrbotConstants
super.onCreate();
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);
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) {
super.onConfigurationChanged(newConfig);
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) {
Intent intent = activity.getIntent();
if (intent == null) // when launched as LAUNCHER
@ -106,6 +76,6 @@ public class OrbotApp extends Application implements OrbotConstants
}
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.setOnPreferenceClickListener(this);
Languages languages = Languages.get(this, R.string.menu_settings, "Settings");
Languages languages = Languages.get(this);
prefLocale.setEntries(languages.getAllNames());
prefLocale.setEntryValues(languages.getSupportedLocales());
prefLocale.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@ -71,7 +71,7 @@ public class SettingsPreferences
String lang = settings.getString("pref_default_locale",
Locale.getDefault().getLanguage());
OrbotApp app = (OrbotApp) getApplication();
app.setNewLocale(language);
Languages.setLanguage(app, language, true);
lang = settings.getString("pref_default_locale",
Locale.getDefault().getLanguage());
OrbotApp.forceChangeLanguage(SettingsPreferences.this);