From 224cd1659142758afc2f5f143e884e75e6f443ef Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 30 May 2015 18:53:30 +0200 Subject: [PATCH] Application language now can be changed in the settings --- android-app/proguard.cfg | 4 + .../main/java/org/solovyev/android/Check.java | 166 +++++++++++++++++ .../android/calculator/ActivityUi.java | 34 +++- .../org/solovyev/android/calculator/App.java | 10 ++ .../calculator/CalculatorApplication.java | 12 ++ .../android/calculator/Preferences.java | 23 ++- .../android/calculator/language/Language.java | 69 +++++++ .../calculator/language/Languages.java | 168 ++++++++++++++++++ .../preferences/PreferencesActivity.java | 45 +++-- .../preferences/PreferencesFragment.java | 48 ++++- .../calculator/wizard/WizardActivity.java | 19 +- .../src/main/res/values/text_preferences.xml | 27 ++- .../src/main/res/values/text_strings.xml | 1 + .../main/res/xml/preferences_appearance.xml | 111 ++++++------ 14 files changed, 636 insertions(+), 101 deletions(-) create mode 100644 android-app/src/main/java/org/solovyev/android/Check.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/language/Language.java create mode 100644 android-app/src/main/java/org/solovyev/android/calculator/language/Languages.java diff --git a/android-app/proguard.cfg b/android-app/proguard.cfg index e0a44ef4..2f65c49c 100644 --- a/android-app/proguard.cfg +++ b/android-app/proguard.cfg @@ -95,6 +95,10 @@ public static int e(...); } +-assumenosideeffects class org.solovyev.android.Check { + *; +} + # #********************************************************************* # diff --git a/android-app/src/main/java/org/solovyev/android/Check.java b/android-app/src/main/java/org/solovyev/android/Check.java new file mode 100644 index 00000000..ea893de8 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/Check.java @@ -0,0 +1,166 @@ +/* + * Copyright 2014 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.android; + +import android.os.Looper; + +import java.util.Collection; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static java.lang.Thread.currentThread; + +public final class Check { + + private static final boolean junit = isJunit(); + + private static boolean isJunit() { + final StackTraceElement[] stackTrace = currentThread().getStackTrace(); + for (StackTraceElement element : stackTrace) { + if (element.getClassName().startsWith("org.junit.")) { + return true; + } + } + return false; + } + + private Check() { + throw new AssertionError(); + } + + public static void isMainThread() { + if (!junit && Looper.getMainLooper() != Looper.myLooper()) { + throw new AssertionException("Should be called on the main thread"); + } + } + + public static void isNotNull(@Nullable Object o) { + isNotNull(o, "Object should not be null"); + } + + public static void isNotNull(@Nullable Object o, @Nonnull String message) { + if (o == null) { + throw new AssertionException(message); + } + } + + public static void notEquals(int expected, int actual) { + if (expected == actual) { + throw new AssertionException("Should not be equal"); + } + } + + public static void equals(int expected, int actual) { + if (expected != actual) { + throw new AssertionException("Should be equal"); + } + } + + public static void equals(@Nullable Object expected, @Nullable Object actual) { + equals(expected, actual, "Should be equal"); + } + + public static void equals(@Nullable Object expected, @Nullable Object actual, @Nonnull String message) { + if (expected == actual) { + // both nulls or same + return; + } + + if (expected != null && actual != null && expected.equals(actual)) { + // equals + return; + } + + throw new AssertionException(message); + } + + public static void isTrue(boolean expression, @Nonnull String message) { + if (!expression) { + throw new AssertionException(message); + } + } + + public static void isFalse(boolean expression, @Nonnull String message) { + if (expression) { + throw new AssertionException(message); + } + } + + public static void isNull(@Nullable Object o) { + isNull(o, "Object should be null"); + } + + public static void isNull(@Nullable Object o, @Nonnull String message) { + if (o != null) { + throw new AssertionException(message); + } + } + + public static void isNotEmpty(@Nullable String s) { + if (s == null || s.length() == 0) { + throw new AssertionException("String should not be empty"); + } + } + + public static void isNotEmpty(@Nullable String[] array) { + if (array == null || array.length == 0) { + throw new AssertionException("Array should not be empty"); + } + } + + public static void isNotEmpty(@Nullable Collection c) { + if (c == null || c.size() == 0) { + throw new AssertionException("Collection should not be empty"); + } + } + + public static void isNotEmpty(@Nullable Map c) { + if (c == null || c.size() == 0) { + throw new AssertionException("Map should not be empty"); + } + } + + public static void same(Object expected, Object actual) { + if (expected != actual) { + throw new AssertionException("Objects should be the same"); + } + } + + public static void isEmpty(@Nullable Collection c) { + if (c != null && !c.isEmpty()) { + throw new AssertionException("Collection should be empty"); + } + } + + public static void shouldNotHappen() { + throw new AssertionException("Should not happen"); + } + + private static final class AssertionException extends RuntimeException { + private AssertionException(@Nonnull String message) { + super(message); + } + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/ActivityUi.java b/android-app/src/main/java/org/solovyev/android/calculator/ActivityUi.java index b66a5046..8c9509de 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/ActivityUi.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/ActivityUi.java @@ -38,8 +38,11 @@ import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import org.solovyev.android.Activities; import org.solovyev.android.Views; +import org.solovyev.android.calculator.language.Language; +import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.sherlock.tabs.ActionBarFragmentTabListener; import javax.annotation.Nonnull; @@ -50,10 +53,13 @@ public class ActivityUi extends BaseUi { private int layoutId; @Nonnull - private Preferences.Gui.Theme theme; + private Preferences.Gui.Theme theme = Preferences.Gui.Theme.default_theme; @Nonnull - private Preferences.Gui.Layout layout; + private Preferences.Gui.Layout layout = Preferences.Gui.Layout.main_calculator; + + @Nonnull + private Language language = Languages.SYSTEM_LANGUAGE; private int selectedNavigationIndex = 0; @@ -73,11 +79,13 @@ public class ActivityUi extends BaseUi { activity.setTheme(theme.getThemeId(activity)); layout = Preferences.Gui.getLayout(preferences); + language = App.getLanguages().getCurrent(); } @Override public void onCreate(@Nonnull Activity activity) { super.onCreate(activity); + App.getLanguages().updateLanguage(activity, false); if (activity instanceof CalculatorEventListener) { Locator.getInstance().getCalculator().addCalculatorEventListener((CalculatorEventListener) activity); @@ -146,16 +154,29 @@ public class ActivityUi extends BaseUi { } public void onResume(@Nonnull Activity activity) { - restartIfThemeChanged(activity, theme); + if (!restartIfThemeChanged(activity, theme)) { + restartIfLanguageChanged(activity, language); + } } - public static void restartIfThemeChanged(@Nonnull Activity activity, @Nonnull Preferences.Gui.Theme oldTheme) { + public static boolean restartIfThemeChanged(@Nonnull Activity activity, @Nonnull Preferences.Gui.Theme oldTheme) { final Preferences.Gui.Theme newTheme = Preferences.Gui.theme.getPreference(App.getPreferences()); final int themeId = oldTheme.getThemeId(activity); final int newThemeId = newTheme.getThemeId(activity); if (themeId != newThemeId) { Activities.restartActivity(activity); + return true; } + return false; + } + + public static boolean restartIfLanguageChanged(@Nonnull Activity activity, @Nonnull Language oldLanguage) { + final Language current = App.getLanguages().getCurrent(); + if (!current.equals(oldLanguage)) { + Activities.restartActivity(activity); + return true; + } + return false; } public void onPause(@Nonnull Activity activity) { @@ -254,6 +275,11 @@ public class ActivityUi extends BaseUi { return theme; } + @Nonnull + public Language getLanguage() { + return language; + } + @Nonnull public Preferences.Gui.Layout getLayout() { return layout; diff --git a/android-app/src/main/java/org/solovyev/android/calculator/App.java b/android-app/src/main/java/org/solovyev/android/calculator/App.java index 23f478a3..c264d6b9 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/App.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/App.java @@ -39,6 +39,7 @@ import org.solovyev.android.Android; import org.solovyev.android.UiThreadExecutor; import org.solovyev.android.Views; import org.solovyev.android.calculator.ga.Ga; +import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.view.ScreenMetrics; import org.solovyev.android.calculator.widget.BaseCalculatorWidgetProvider; import org.solovyev.android.calculator.widget.CalculatorWidgetProvider; @@ -117,6 +118,9 @@ public final class App { @Nonnull private static volatile ScreenMetrics screenMetrics; + @Nonnull + private static final Languages languages = new Languages(); + private App() { throw new AssertionError(); } @@ -177,6 +181,7 @@ public final class App { Android.enableComponent(application, CalculatorWidgetProvider4x4.class, false); } } + App.languages.init(App.preferences); App.initialized = true; } else { @@ -277,6 +282,11 @@ public final class App { return Preferences.Gui.getTheme(getPreferences()); } + @Nonnull + public static Languages getLanguages() { + return languages; + } + public static boolean isLg() { if (lg == null) { lg = "lge".equalsIgnoreCase(Build.BRAND) || "lge".equalsIgnoreCase(Build.MANUFACTURER); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java index 27795036..0718d590 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java @@ -37,6 +37,7 @@ import org.acra.ReportingInteractionMode; import org.acra.annotation.ReportsCrashes; import org.solovyev.android.Android; import org.solovyev.android.calculator.history.AndroidCalculatorHistory; +import org.solovyev.android.calculator.language.Language; import org.solovyev.android.calculator.model.AndroidCalculatorEngine; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenStartActivity; import org.solovyev.android.calculator.plot.AndroidCalculatorPlotter; @@ -48,6 +49,7 @@ import org.solovyev.common.msg.MessageType; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; @@ -139,8 +141,10 @@ public class CalculatorApplication extends android.app.Application implements Sh preferences.registerOnSharedPreferenceChangeListener(this); setTheme(preferences); + setLanguageInitially(); super.onCreate(); + App.getLanguages().updateLanguage(this, true); if (!Preferences.Ga.initialReportDone.getPreference(preferences)) { App.getGa().reportInitially(preferences); @@ -199,6 +203,14 @@ public class CalculatorApplication extends android.app.Application implements Sh }, 100, TimeUnit.MILLISECONDS); } + private void setLanguageInitially() { + // should be called before onCreate() + final Language language = App.getLanguages().getCurrent(); + if (!language.isSystem() && !language.locale.equals(Locale.getDefault())) { + Locale.setDefault(language.locale); + } + } + private void setTheme(@Nonnull SharedPreferences preferences) { final Preferences.Gui.Theme theme = Preferences.Gui.getTheme(preferences); setTheme(theme.getThemeId()); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/Preferences.java b/android-app/src/main/java/org/solovyev/android/calculator/Preferences.java index 61eeb02f..d2f19906 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/Preferences.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/Preferences.java @@ -29,19 +29,28 @@ import android.graphics.Color; import android.support.annotation.StyleRes; import android.util.SparseArray; import android.view.ContextThemeWrapper; -import jscl.AngleUnit; -import jscl.NumeralBase; + +import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.model.AndroidCalculatorEngine; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService; import org.solovyev.android.calculator.preferences.PurchaseDialogActivity; import org.solovyev.android.calculator.wizard.WizardActivity; -import org.solovyev.android.prefs.*; +import org.solovyev.android.prefs.BooleanPreference; +import org.solovyev.android.prefs.IntegerPreference; +import org.solovyev.android.prefs.LongPreference; +import org.solovyev.android.prefs.NumberToStringPreference; +import org.solovyev.android.prefs.Preference; +import org.solovyev.android.prefs.StringPreference; + +import java.text.DecimalFormatSymbols; +import java.util.Locale; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.text.DecimalFormatSymbols; -import java.util.Locale; + +import jscl.AngleUnit; +import jscl.NumeralBase; import static org.solovyev.android.Android.isPhoneModel; import static org.solovyev.android.DeviceModel.samsung_galaxy_s; @@ -80,8 +89,8 @@ public final class Preferences { public static final Preference theme = StringPreference.ofEnum("org.solovyev.android.calculator.CalculatorActivity_calc_theme", Theme.material_theme, Theme.class); public static final Preference layout = StringPreference.ofEnum("org.solovyev.android.calculator.CalculatorActivity_calc_layout", Layout.simple, Layout.class); + public static final Preference language = StringPreference.of("gui.language", Languages.SYSTEM_LANGUAGE_CODE); public static final Preference feedbackWindowShown = BooleanPreference.of("feedback_window_shown", false); - public static final Preference notesppAnnounceShown = BooleanPreference.of("notespp_announce_shown", false); public static final Preference showReleaseNotes = BooleanPreference.of("org.solovyev.android.calculator.CalculatorActivity_show_release_notes", true); public static final Preference usePrevAsBack = BooleanPreference.of("org.solovyev.android.calculator.CalculatorActivity_use_back_button_as_prev", false); public static final Preference showEqualsButton = BooleanPreference.of("showEqualsButton", true); @@ -264,13 +273,13 @@ public final class Preferences { Gui.layout.putDefault(preferences); } applyDefaultPreference(preferences, Gui.feedbackWindowShown); - applyDefaultPreference(preferences, Gui.notesppAnnounceShown); applyDefaultPreference(preferences, Gui.showReleaseNotes); applyDefaultPreference(preferences, Gui.usePrevAsBack); applyDefaultPreference(preferences, Gui.showEqualsButton); applyDefaultPreference(preferences, Gui.autoOrientation); applyDefaultPreference(preferences, Gui.hideNumeralBaseDigits); applyDefaultPreference(preferences, Gui.preventScreenFromFading); + applyDefaultPreference(preferences, Gui.language); applyDefaultPreference(preferences, Graph.plotImag); applyDefaultPreference(preferences, History.showIntermediateCalculations); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/language/Language.java b/android-app/src/main/java/org/solovyev/android/calculator/language/Language.java new file mode 100644 index 00000000..2431d459 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/language/Language.java @@ -0,0 +1,69 @@ +package org.solovyev.android.calculator.language; + +import android.content.Context; +import android.text.TextUtils; + +import org.solovyev.android.calculator.R; + +import java.util.Locale; + +import javax.annotation.Nonnull; + +public final class Language { + @Nonnull + public final String code; + + @Nonnull + public final Locale locale; + + @Nonnull + final String name; + + public Language(@Nonnull String code, @Nonnull Locale locale) { + this.code = code; + this.locale = locale; + this.name = makeName(code, locale); + } + + @Nonnull + public String getName(@Nonnull Context context) { + if (!isSystem()) { + return name; + } else { + return context.getString(R.string.cpp_system_language) + " (" + locale.getDisplayLanguage(locale) + ")"; + } + } + + @Nonnull + private static String makeName(@Nonnull String code, @Nonnull Locale locale) { + if (code.equals(Languages.SYSTEM_LANGUAGE_CODE)) { + return ""; + } + + final int underscore = code.indexOf("_"); + if (underscore >= 0 && TextUtils.isEmpty(locale.getDisplayCountry(locale))) { + return locale.getDisplayName(locale) + " (" + code.substring(underscore + 1) + ")"; + } + + return locale.getDisplayName(locale); + } + + public boolean isSystem() { + return code.equals(Languages.SYSTEM_LANGUAGE_CODE); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Language language = (Language) o; + return code.equals(language.code); + + } + + @Override + public int hashCode() { + return code.hashCode(); + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/language/Languages.java b/android-app/src/main/java/org/solovyev/android/calculator/language/Languages.java new file mode 100644 index 00000000..31c293be --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/language/Languages.java @@ -0,0 +1,168 @@ +package org.solovyev.android.calculator.language; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; + +import org.solovyev.android.Check; +import org.solovyev.android.calculator.App; +import org.solovyev.android.calculator.Preferences; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class Languages implements SharedPreferences.OnSharedPreferenceChangeListener { + + @Nonnull + private static final Locale[] locales = Locale.getAvailableLocales(); + @Nonnull + public static final String SYSTEM_LANGUAGE_CODE = "00"; + @Nonnull + public static final Language SYSTEM_LANGUAGE = new Language(SYSTEM_LANGUAGE_CODE, Locale.getDefault()); + @Nonnull + private final List list = new ArrayList<>(); + + public void init(@Nonnull SharedPreferences preferences) { + preferences.registerOnSharedPreferenceChangeListener(this); + } + + @Nonnull + public List getList() { + Check.isMainThread(); + if (list.isEmpty()) { + loadList(); + } + return list; + } + + private void loadList() { + Check.isMainThread(); + Check.isEmpty(list); + tryAddLanguage("ar"); + tryAddLanguage("cs"); + tryAddLanguage("en"); + tryAddLanguage("es_ES"); + tryAddLanguage("de"); + tryAddLanguage("fi"); + tryAddLanguage("fr"); + tryAddLanguage("it"); + tryAddLanguage("it"); + tryAddLanguage("pl"); + tryAddLanguage("pt_BR"); + tryAddLanguage("pt_PT"); + tryAddLanguage("ru"); + tryAddLanguage("vi"); + tryAddLanguage("uk"); + tryAddLanguage("ja"); + tryAddLanguage("zh_CN"); + tryAddLanguage("zh_TW"); + Collections.sort(list, new Comparator() { + @Override + public int compare(Language lhs, Language rhs) { + return lhs.name.compareTo(rhs.name); + } + }); + list.add(0, SYSTEM_LANGUAGE); + } + + private void tryAddLanguage(@Nonnull String locale) { + final Language language = makeLanguage(locale); + if (language != null) { + list.add(language); + } + } + + @Nullable + private static Language makeLanguage(@Nonnull String localeId) { + final Locale locale = findLocaleById(localeId); + if (locale == null) { + return null; + } + return new Language(localeId, locale); + } + + @Nullable + private static Locale findLocaleById(@Nonnull String id) { + for (Locale locale : locales) { + if (TextUtils.equals(locale.toString(), id)) { + return locale; + } + } + + final String language; + final int underscore = id.indexOf("_"); + if (underscore >= 0) { + language = id.substring(0, underscore); + } else { + language = id; + } + + for (Locale locale : locales) { + if (TextUtils.equals(locale.getLanguage(), language)) { + return locale; + } + } + + Log.d("Languages", "No locale found for " + id); + return null; + } + + @Nonnull + public Language getCurrent() { + return get(Preferences.Gui.language.getPreference(App.getPreferences())); + } + + @Nonnull + public Language get(@Nonnull String code) { + Language language = findLanguageByCode(code); + if (language != null) { + return language; + } + return SYSTEM_LANGUAGE; + } + + @Nullable + private Language findLanguageByCode(@Nonnull String code) { + for (Language language : getList()) { + if (TextUtils.equals(language.code, code)) { + return language; + } + } + return null; + } + + @Override + public void onSharedPreferenceChanged(@Nonnull SharedPreferences p, String key) { + if (Preferences.Gui.language.isSameKey(key)) { + updateLanguage(App.getApplication(), false); + } + } + + public void updateLanguage(@Nonnull Context context, boolean initial) { + final Language language = getCurrent(); + // we don't need to set system language while starting up the app + if (!initial || !language.isSystem()) { + if (!Locale.getDefault().equals(language.locale)) { + Locale.setDefault(language.locale); + } + + final Resources r = context.getResources(); + final DisplayMetrics dm = r.getDisplayMetrics(); + final Configuration c = r.getConfiguration(); + if (c.locale == null || !c.locale.equals(language.locale)) { + c.locale = language.locale; + r.updateConfiguration(c, dm); + } + } + } +} \ No newline at end of file diff --git a/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java b/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java index 84d3fbab..5810b684 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java @@ -6,7 +6,6 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.StringRes; import android.support.annotation.XmlRes; -import android.text.TextUtils; import android.util.SparseArray; import org.solovyev.android.calculator.ActivityUi; @@ -27,15 +26,27 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc static final String EXTRA_PREFERENCE_TITLE = "preference-title"; @Nonnull - private static final SparseArray preferences = new SparseArray<>(); + private static final SparseArray preferences = new SparseArray<>(); static { - preferences.append(R.xml.preferences, "screen-main"); - preferences.append(R.xml.preferences_calculations, "screen-calculations"); - preferences.append(R.xml.preferences_appearance, "screen-appearance"); - preferences.append(R.xml.preferences_plot, "screen-plot"); - preferences.append(R.xml.preferences_other, "screen-other"); - preferences.append(R.xml.preferences_onscreen, "screen-onscreen"); + preferences.append(R.xml.preferences, new PrefDef("screen-main", R.string.c_app_settings)); + preferences.append(R.xml.preferences_calculations, new PrefDef("screen-calculations", R.string.c_prefs_calculations_category)); + preferences.append(R.xml.preferences_appearance, new PrefDef("screen-appearance", R.string.c_prefs_appearance_category)); + preferences.append(R.xml.preferences_plot, new PrefDef("screen-plot", R.string.prefs_graph_screen_title)); + preferences.append(R.xml.preferences_other, new PrefDef("screen-other", R.string.c_prefs_other_category)); + preferences.append(R.xml.preferences_onscreen, new PrefDef("screen-onscreen", R.string.prefs_onscreen_title)); + } + + static class PrefDef { + @Nonnull + public final String id; + @StringRes + public final int title; + + PrefDef(@Nonnull String id, int title) { + this.id = id; + this.title = title; + } } @Nonnull @@ -53,8 +64,8 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc App.getPreferences().registerOnSharedPreferenceChangeListener(this); final Intent intent = getIntent(); - final String preferenceTitle = intent.getStringExtra(EXTRA_PREFERENCE_TITLE); - if (!TextUtils.isEmpty(preferenceTitle)) { + final int preferenceTitle = intent.getIntExtra(EXTRA_PREFERENCE_TITLE, 0); + if (preferenceTitle != 0) { setTitle(preferenceTitle); } @@ -72,8 +83,12 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (!paused && Preferences.Gui.theme.isSameKey(key)) { - ActivityUi.restartIfThemeChanged(this, ui.getTheme()); + if (!paused) { + if (Preferences.Gui.theme.isSameKey(key)) { + ActivityUi.restartIfThemeChanged(this, ui.getTheme()); + } else if (Preferences.Gui.language.isSameKey(key)) { + ActivityUi.restartIfLanguageChanged(this, ui.getLanguage()); + } } } @@ -97,7 +112,7 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc } @Nonnull - static SparseArray getPreferences() { + static SparseArray getPreferences() { return preferences; } @@ -107,11 +122,11 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc } @Nonnull - static Intent makeIntent(@Nonnull Context context, int preference, int title) { + static Intent makeIntent(@Nonnull Context context, @XmlRes int preference, @StringRes int title) { final Intent intent = new Intent(context, PreferencesActivity.class); intent.putExtra(EXTRA_PREFERENCE, preference); if (title != 0) { - intent.putExtra(EXTRA_PREFERENCE_TITLE, context.getString(title)); + intent.putExtra(EXTRA_PREFERENCE_TITLE, title); } return intent; } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java index 2d4d4a06..45b0f0d9 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; +import android.preference.ListPreference; import android.preference.Preference; import android.util.SparseArray; import android.view.LayoutInflater; @@ -13,12 +14,17 @@ import android.widget.ListView; import org.solovyev.android.calculator.AdView; import org.solovyev.android.calculator.App; import org.solovyev.android.calculator.CalculatorApplication; +import org.solovyev.android.calculator.Preferences; import org.solovyev.android.calculator.R; +import org.solovyev.android.calculator.language.Language; +import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.checkout.BillingRequests; import org.solovyev.android.checkout.Checkout; import org.solovyev.android.checkout.ProductTypes; import org.solovyev.android.checkout.RequestListener; +import java.util.List; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -51,12 +57,12 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc App.getPreferences().registerOnSharedPreferenceChangeListener(this); } - private void setPreferenceIntent(int xml, @Nonnull String name) { - final Preference preference = findPreference(name); + private void setPreferenceIntent(int xml, @Nonnull PreferencesActivity.PrefDef def) { + final Preference preference = findPreference(def.id); if (preference != null) { final Intent intent = new Intent(getActivity(), PreferencesActivity.class); intent.putExtra(PreferencesActivity.EXTRA_PREFERENCE, xml); - intent.putExtra(PreferencesActivity.EXTRA_PREFERENCE_TITLE, preference.getTitle()); + intent.putExtra(PreferencesActivity.EXTRA_PREFERENCE_TITLE, def.title); preference.setIntent(intent); } } @@ -67,11 +73,11 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc final int preference = getPreferencesResId(); if (preference == R.xml.preferences) { - final SparseArray preferences = PreferencesActivity.getPreferences(); + final SparseArray preferences = PreferencesActivity.getPreferences(); for (int i = 0; i < preferences.size(); i++) { final int xml = preferences.keyAt(i); - final String name = preferences.valueAt(i); - setPreferenceIntent(xml, name); + final PreferencesActivity.PrefDef def = preferences.valueAt(i); + setPreferenceIntent(xml, def); } final Preference restartWizardPreference = findPreference("restart_wizard"); restartWizardPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @@ -96,6 +102,8 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc } } + prepareLanguagePreference(preference); + getCheckout().whenReady(new Checkout.ListenerAdapter() { @Override public void onReady(@Nonnull BillingRequests requests) { @@ -121,6 +129,34 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc onSharedPreferenceChanged(preferences, roundResult.getKey()); } + private void prepareLanguagePreference(int preference) { + if (preference != R.xml.preferences_appearance) { + return; + } + + final ListPreference language = (ListPreference) preferenceManager.findPreference(Preferences.Gui.language.getKey()); + final Languages languages = App.getLanguages(); + final List languagesList = languages.getList(); + final CharSequence[] entries = new CharSequence[languagesList.size()]; + final CharSequence[] entryValues = new CharSequence[languagesList.size()]; + for (int i = 0; i < languagesList.size(); i++) { + final Language l = languagesList.get(i); + entries[i] = l.getName(getActivity()); + entryValues[i] = l.code; + } + language.setEntries(entries); + language.setEntryValues(entryValues); + language.setSummary(languages.getCurrent().getName(getActivity())); + language.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final Language l = languages.get((String) newValue); + language.setSummary(l.getName(getActivity())); + return true; + } + }); + } + @Nonnull private Checkout getCheckout() { return ((PreferencesActivity) getActivity()).getCheckout(); diff --git a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java index 4abe9c1b..a05e8748 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/wizard/WizardActivity.java @@ -8,9 +8,22 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; + import com.viewpagerindicator.PageIndicator; -import org.solovyev.android.calculator.*; -import org.solovyev.android.wizard.*; + +import org.solovyev.android.calculator.ActivityUi; +import org.solovyev.android.calculator.App; +import org.solovyev.android.calculator.BaseActivity; +import org.solovyev.android.calculator.CalculatorApplication; +import org.solovyev.android.calculator.Preferences; +import org.solovyev.android.calculator.R; +import org.solovyev.android.wizard.ListWizardFlow; +import org.solovyev.android.wizard.Wizard; +import org.solovyev.android.wizard.WizardFlow; +import org.solovyev.android.wizard.WizardStep; +import org.solovyev.android.wizard.WizardUi; +import org.solovyev.android.wizard.Wizards; +import org.solovyev.android.wizard.WizardsAware; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -190,6 +203,8 @@ public class WizardActivity extends BaseActivity implements WizardsAware, Shared public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { if (Preferences.Gui.theme.isSameKey(key)) { ActivityUi.restartIfThemeChanged(this, ui.getTheme()); + } else if (Preferences.Gui.language.isSameKey(key)) { + ActivityUi.restartIfLanguageChanged(this, ui.getLanguage()); } } diff --git a/android-app/src/main/res/values/text_preferences.xml b/android-app/src/main/res/values/text_preferences.xml index cd34b887..b92b000c 100644 --- a/android-app/src/main/res/values/text_preferences.xml +++ b/android-app/src/main/res/values/text_preferences.xml @@ -2,21 +2,20 @@ - Auto-rotate screen - If turned on app will rotate screen with device - Show equals button - If turned on equals button is shown - Hide digits of other numeral systems - If turned on numeral base digits of other numeral bases will be - hidden - - Show intermediate calculations in history - If turned on all calculations will be shown in the + Auto-rotate screen + If turned on app will rotate screen with device + Show equals button + If turned on equals button is shown + Hide digits of other numeral systems + If turned on numeral base digits of other numeral bases will be hidden + Show intermediate calculations in history + If turned on all calculations will be shown in the history screen - Result is calculated while typing - If turned on calculations are done automatically while typing - Prevent screen from fading - If turned on screen will not fade while using the app + Result is calculated while typing + If turned on calculations are done automatically while typing + Prevent screen from fading + If turned on screen will not fade while using the app + Language diff --git a/android-app/src/main/res/values/text_strings.xml b/android-app/src/main/res/values/text_strings.xml index 86862d08..e1c3259a 100644 --- a/android-app/src/main/res/values/text_strings.xml +++ b/android-app/src/main/res/values/text_strings.xml @@ -231,4 +231,5 @@ Clear New in %1$s version Do you want to try new Material themes? Choose them from the list: + System language diff --git a/android-app/src/main/res/xml/preferences_appearance.xml b/android-app/src/main/res/xml/preferences_appearance.xml index 06a138f9..a8fa1478 100644 --- a/android-app/src/main/res/xml/preferences_appearance.xml +++ b/android-app/src/main/res/xml/preferences_appearance.xml @@ -23,69 +23,74 @@ --> + xmlns:m="http://schemas.android.com/apk/res-auto"> - + - + - + - + - + - + + + - + - + - + - + \ No newline at end of file