diff --git a/src/keepass2android/Utils/Util.cs b/src/keepass2android/Utils/Util.cs
index 5ffb0156..b849df3b 100644
--- a/src/keepass2android/Utils/Util.cs
+++ b/src/keepass2android/Utils/Util.cs
@@ -78,8 +78,8 @@ namespace keepass2android
{
if (!languageLoaded)
{
- var language = PreferenceManager.GetDefaultSharedPreferences(c).GetString(c.GetString(Resource.String.app_language_pref_key), null);
- Language = language;
+ var langPref = PreferenceManager.GetDefaultSharedPreferences(c).GetString(c.GetString(Resource.String.app_language_pref_key), null);
+ Language = LanguageEntry.PrefCodeToLanguage(langPref);
}
return Language;
}
@@ -109,8 +109,68 @@ namespace keepass2android
}
}
+ ///
+ /// A small container/wrapper that helps with the building and sorting of the
+ /// Language Preference list, and interoperability between it and LocaleManager
+ ///
+ class LanguageEntry
+ {
+ // "System language" preference code; maps to LocaleManager.Language = null
+ private const string SYS_LANG_CODE = "SysLang";
- public class Util {
+ // Preference code (2-char lowercase, or SYS_LANG_CODE)
+ public readonly string Code;
+ // Localized display name
+ public readonly string Name;
+ // True if this LanguageEntry is the "System language", false otherwise
+ public readonly bool IsSystem;
+
+ ///
+ /// Creates a LanguageEntry from a Locale
+ ///
+ ///
+ ///
+ public static LanguageEntry OfLocale(Java.Util.Locale from)
+ {
+ return new LanguageEntry(from.Language, from.DisplayLanguage, false);
+ }
+
+ ///
+ /// Creates the "System language" LanguageEntry with the given localized display name,
+ /// special preference code, marked as a System entry.
+ ///
+ ///
+ ///
+ public static LanguageEntry SystemDefault(string displayName)
+ {
+ return new LanguageEntry(SYS_LANG_CODE, displayName, true);
+ }
+
+ private LanguageEntry(string code, string name, bool isSystem)
+ {
+ this.Code = code;
+ this.Name = name;
+ this.IsSystem = isSystem;
+ }
+
+ ///
+ /// Converts a language preference code to a code that is compatible with LocaleManager.Language
+ ///
+ ///
+ /// A converted code, possibly null
+ public static string PrefCodeToLanguage(string code)
+ {
+ return string.IsNullOrEmpty(code) || SYS_LANG_CODE.Equals(code) ? null : code;
+ }
+
+ public override string ToString()
+ {
+ return "{" + this.Code + ":" + this.Name + "}";
+ }
+ }
+
+
+ public class Util {
public const String KeyFilename = "fileName";
diff --git a/src/keepass2android/settings/DatabaseSettingsActivity.cs b/src/keepass2android/settings/DatabaseSettingsActivity.cs
index d857824a..608154da 100644
--- a/src/keepass2android/settings/DatabaseSettingsActivity.cs
+++ b/src/keepass2android/settings/DatabaseSettingsActivity.cs
@@ -41,10 +41,12 @@ using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Interfaces;
using System.Collections.Generic;
+
namespace keepass2android
{
//http://stackoverflow.com/a/27422401/292233
- public class SettingsFragment : PreferenceFragment
+#pragma warning disable CS0618 // Type or member is obsolete
+ public class SettingsFragment : PreferenceFragment
{
@@ -177,36 +179,8 @@ namespace keepass2android
FindPreference(GetString(Resource.String.DebugLog_send_key)).PreferenceClick += OnSendDebug;
HashSet supportedLocales = new HashSet() { "en", "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "eu", "fa", "fi", "fr", "gl", "he", "hr", "hu", "id", "in", "it", "iw", "ja", "ko", "ml", "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "tr", "uk", "vi", "zh" };
-
- ListPreference appLanguagePref = (ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key));
-
- var localesByCode = new System.Collections.Generic.Dictionary>();
- foreach (var loc in Java.Util.Locale.GetAvailableLocales())
- {
- if (!supportedLocales.Contains(loc.Language))
- continue;
- if (!localesByCode.ContainsKey(loc.Language))
- {
- localesByCode[loc.Language] = new List();
- }
- localesByCode[loc.Language].Add(loc);
-
- }
- var localesByCodeUnique = localesByCode.Select(l => new KeyValuePair(l.Key, l.Value.First())).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
- List>> codesWithMultiple = localesByCode.Where(l => l.Value.Count > 1).ToList();
- List> localesByLanguageList = localesByCodeUnique
- .OrderBy(kvp => kvp.Value.DisplayLanguage).ToList();
- appLanguagePref.SetEntries(localesByLanguageList.Select(kvp => kvp.Value.DisplayLanguage).ToArray());
- appLanguagePref.SetEntryValues(localesByLanguageList.Select(kvp => kvp.Value.Language).ToArray());
- string languageCode = appLanguagePref.Value;
- string summary = GetDisplayLanguage(localesByCodeUnique, languageCode);
- ((ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key))).Summary = summary;
- appLanguagePref.PreferenceChange += (sender, args) =>
- {
- ((ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key))).Summary = GetDisplayLanguage(localesByCodeUnique, (string)args.NewValue);
- LocaleManager.Language = (string)args.NewValue;
- };
-
+ var languagePref = (ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key));
+ new AppLanguageManager(this, languagePref, supportedLocales);
UpdateAutofillPref();
@@ -347,16 +321,6 @@ namespace keepass2android
}
- private string GetDisplayLanguage(Dictionary localesByCode, string languageCode)
- {
- return languageCode != null && localesByCode.ContainsKey(languageCode) ? localesByCode[languageCode]?.DisplayLanguage : GetString(Resource.String.SystemLanguage);
- }
-
- private void AppLanguagePref_PreferenceChange(object sender, Preference.PreferenceChangeEventArgs e)
- {
- throw new NotImplementedException();
- }
-
private void UpdateAutofillPref()
{
var autofillScreen = FindPreference(GetString(Resource.String.AutoFill_prefs_screen_key));
@@ -897,10 +861,102 @@ namespace keepass2android
}
-
///
- /// Activity to configure the application and database settings. The database must be unlocked, and this activity will close if it becomes locked.
- ///
+ ///
+ /// A helper class that manages language preference display and selection.
+ ///
+ ///
+ /// The idea is to provide a ListPreference with a "System language" item at the top, followed by
+ /// the localized list of supported language names. The items are backed by their corresponding "code".
+ /// For a langauge that's the 2-char lowercase language code, which is exactly the same code that
+ /// LocaleManager.Language expects.
+ ///
+ ///
+ /// "System language" is a special case. LocaleManager.Language expects null, but ListPreference
+ /// does not support null as a valid code. To work around this, LanguageEntry.SYS_LANG_CODE
+ /// is used as the preference code. LanguageEntry.PrefCodeToLanguage(string) is used to convert the
+ /// preference codes to language codes as needed.
+ ///
+ ///
+ internal class AppLanguageManager
+ {
+ private readonly PreferenceFragment _fragment;
+ private readonly ListPreference _langPref;
+ private readonly Dictionary _langEntriesByCodeUnique;
+
+ public AppLanguageManager(PreferenceFragment fragment, ListPreference langPref, HashSet supportedLocales)
+ {
+ this._fragment = fragment;
+ this._langPref = langPref;
+ this._langEntriesByCodeUnique = CreateCodeToEntryMapping(fragment, supportedLocales);
+
+ ConfigureLanguageList();
+ }
+
+ private static Dictionary CreateCodeToEntryMapping(PreferenceFragment fragment, HashSet supportedLocales)
+ {
+ var localesByCode = new Dictionary>();
+ foreach (var loc in Java.Util.Locale.GetAvailableLocales())
+ {
+ if (!supportedLocales.Contains(loc.Language))
+ continue;
+ if (!localesByCode.ContainsKey(loc.Language))
+ {
+ localesByCode[loc.Language] = new List();
+ }
+ localesByCode[loc.Language].Add(loc);
+ }
+
+ var langEntriesByCodeUnique = localesByCode
+ .Select(l => new KeyValuePair(l.Key, LanguageEntry.OfLocale(l.Value.First())))
+ .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+
+ var sysLangEntry = LanguageEntry.SystemDefault(fragment.GetString(Resource.String.SystemLanguage));
+ langEntriesByCodeUnique.Add(sysLangEntry.Code, sysLangEntry);
+
+ return langEntriesByCodeUnique;
+ }
+
+ private void ConfigureLanguageList()
+ {
+ List> langEntriesList = _langEntriesByCodeUnique
+ .OrderByDescending(kvp => kvp.Value.IsSystem)
+ .ThenBy(kvp => kvp.Value.Name)
+ .ToList();
+
+ _langPref.SetEntries(langEntriesList
+ .Select(kvp => kvp.Value.Name)
+ .ToArray());
+ _langPref.SetEntryValues(langEntriesList
+ .Select(kvp => kvp.Value.Code)
+ .ToArray());
+
+ _langPref.Summary = GetDisplayLanguage(LanguageEntry.PrefCodeToLanguage(_langPref.Value));
+ _langPref.PreferenceChange += AppLanguagePrefChange;
+ }
+
+ private string GetDisplayLanguage(string languageCode)
+ {
+ if (languageCode != null && this._langEntriesByCodeUnique.ContainsKey(languageCode))
+ return this._langEntriesByCodeUnique[languageCode]?.Name;
+ else
+ return _fragment.GetString(Resource.String.SystemLanguage);
+ }
+
+ private void AppLanguagePrefChange(object sender, Preference.PreferenceChangeEventArgs args)
+ {
+ string langCode = LanguageEntry.PrefCodeToLanguage((string)args.NewValue);
+ LocaleManager.Language = langCode;
+ _langPref.Summary = GetDisplayLanguage(langCode);
+ }
+ }
+
+#pragma warning restore CS0618 // Type or member is obsolete
+
+
+ ///
+ /// Activity to configure the application and database settings. The database must be unlocked, and this activity will close if it becomes locked.
+ ///
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class DatabaseSettingsActivity : LockCloseActivity
{