From 44e894f97bcbc29af7c4822eb56f31b195a3d0d5 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Wed, 13 Jul 2016 22:41:29 -0500 Subject: [PATCH] Add Locale wrapper --- .../astrid/ui/MultilineListPreference.java | 4 +- .../org/tasks/dialogs/AlertDialogBuilder.java | 12 +- .../java/org/tasks/dialogs/DialogBuilder.java | 7 +- .../tasks/injection/ApplicationModule.java | 11 +- .../injection/InjectingAppCompatActivity.java | 4 +- .../InjectingPreferenceActivity.java | 4 +- src/main/java/org/tasks/locale/Locale.java | 135 ++++++++++++++++++ .../org/tasks/locale/LocalePickerDialog.java | 28 ++-- .../java/org/tasks/locale/LocaleUtils.java | 89 ------------ .../AppCompatPreferenceActivity.java | 2 - .../preferences/BaseBasicPreferences.java | 22 ++- src/main/java/org/tasks/time/DateTime.java | 5 +- .../widget/ScrollableWidgetUpdateService.java | 5 +- 13 files changed, 189 insertions(+), 139 deletions(-) create mode 100644 src/main/java/org/tasks/locale/Locale.java delete mode 100644 src/main/java/org/tasks/locale/LocaleUtils.java diff --git a/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java b/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java index a0f0e5b3a..e6a164175 100644 --- a/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java +++ b/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java @@ -11,7 +11,7 @@ import android.preference.ListPreference; import android.util.AttributeSet; import android.view.View; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import timber.log.Timber; @@ -35,7 +35,7 @@ public class MultilineListPreference extends ListPreference { @Override protected void showDialog(Bundle state) { super.showDialog(state); - LocaleUtils.fixDialogButtons(getDialog()); + Locale.INSTANCE.fixDialogButtonDirectionality(getDialog()); } @Override diff --git a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java index 0850870b2..bf1dbc796 100644 --- a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java +++ b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java @@ -7,7 +7,7 @@ import android.view.ContextThemeWrapper; import android.view.View; import android.widget.ListAdapter; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import org.tasks.themes.Theme; import java.util.List; @@ -19,10 +19,12 @@ public class AlertDialogBuilder { private final AlertDialog.Builder builder; private final Context context; private final Theme theme; + private final Locale locale; - public AlertDialogBuilder(Context context, Theme theme) { + public AlertDialogBuilder(Context context, Theme theme, Locale locale) { this.context = context; this.theme = theme; + this.locale = locale; ContextThemeWrapper wrapper = theme.getThemedDialog(context); theme.applyToContext(wrapper); builder = new AlertDialog.Builder(wrapper); @@ -83,7 +85,7 @@ public class AlertDialogBuilder { private String[] addDirectionality(String[] strings) { if (atLeastJellybeanMR1()) { - final char directionalityMark = LocaleUtils.getDirectionalityMark(); + final char directionalityMark = locale.getDirectionalityMark(); for (int i = 0 ; i < strings.length ; i++) { strings[i] = directionalityMark + strings[i]; } @@ -126,14 +128,14 @@ public class AlertDialogBuilder { AlertDialog dialog = create(); theme.applyToContext(dialog.getListView().getContext()); dialog.show(); - LocaleUtils.fixDialogButtons(dialog); + locale.fixDialogButtonDirectionality(dialog); return dialog; } public AlertDialog show() { AlertDialog dialog = create(); dialog.show(); - LocaleUtils.fixDialogButtons(dialog); + locale.fixDialogButtonDirectionality(dialog); return dialog; } } diff --git a/src/main/java/org/tasks/dialogs/DialogBuilder.java b/src/main/java/org/tasks/dialogs/DialogBuilder.java index b1bff8c8c..ba4381d6c 100644 --- a/src/main/java/org/tasks/dialogs/DialogBuilder.java +++ b/src/main/java/org/tasks/dialogs/DialogBuilder.java @@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable; import com.todoroo.andlib.utility.AndroidUtilities; +import org.tasks.locale.Locale; import org.tasks.themes.Theme; import javax.inject.Inject; @@ -13,15 +14,17 @@ import javax.inject.Inject; public class DialogBuilder { private final Activity activity; private final Theme theme; + private final Locale locale; @Inject - public DialogBuilder(Activity activity, Theme theme) { + public DialogBuilder(Activity activity, Theme theme, Locale locale) { this.activity = activity; this.theme = theme; + this.locale = locale; } public AlertDialogBuilder newDialog() { - return new AlertDialogBuilder(activity, theme); + return new AlertDialogBuilder(activity, theme, locale); } @Deprecated diff --git a/src/main/java/org/tasks/injection/ApplicationModule.java b/src/main/java/org/tasks/injection/ApplicationModule.java index dbdca621a..b51c243ea 100644 --- a/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/src/main/java/org/tasks/injection/ApplicationModule.java @@ -7,7 +7,7 @@ import android.preference.PreferenceManager; import org.tasks.ErrorReportingSingleThreadExecutor; import org.tasks.R; import org.tasks.analytics.Tracker; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import org.tasks.themes.ThemeCache; import org.tasks.ui.CheckBoxes; import org.tasks.ui.WidgetCheckBoxes; @@ -30,8 +30,13 @@ public class ApplicationModule { public ApplicationModule(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String language = prefs.getString(context.getString(R.string.p_language), null); - LocaleUtils.setLocale(language); - this.context = LocaleUtils.createConfigurationContext(context.getApplicationContext()); + Locale.INSTANCE = new Locale(java.util.Locale.getDefault(), language); + this.context = Locale.INSTANCE.createConfigurationContext(context.getApplicationContext()); + } + + @Provides + public Locale getLocale() { + return Locale.INSTANCE; } @Provides diff --git a/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java b/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java index 34eda4cb8..c77a6cf17 100644 --- a/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java +++ b/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java @@ -4,7 +4,7 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import org.tasks.analytics.Tracker; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import javax.inject.Inject; @@ -14,7 +14,7 @@ public abstract class InjectingAppCompatActivity extends AppCompatActivity imple @Inject Tracker tracker; public InjectingAppCompatActivity() { - LocaleUtils.applyOverrideConfiguration(this); + Locale.INSTANCE.applyOverrideConfiguration(this); } @Override diff --git a/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java b/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java index 8326c9796..db6f64b78 100644 --- a/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java +++ b/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java @@ -13,7 +13,7 @@ import android.widget.LinearLayout; import org.tasks.R; import org.tasks.analytics.Tracker; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import org.tasks.preferences.AppCompatPreferenceActivity; import org.tasks.themes.Theme; import org.tasks.ui.MenuColorizer; @@ -32,7 +32,7 @@ public abstract class InjectingPreferenceActivity extends AppCompatPreferenceAct @Inject Tracker tracker; public InjectingPreferenceActivity() { - LocaleUtils.applyOverrideConfiguration(this); + Locale.INSTANCE.applyOverrideConfiguration(this); } @Override diff --git a/src/main/java/org/tasks/locale/Locale.java b/src/main/java/org/tasks/locale/Locale.java new file mode 100644 index 000000000..b10be0921 --- /dev/null +++ b/src/main/java/org/tasks/locale/Locale.java @@ -0,0 +1,135 @@ +package org.tasks.locale; + +import android.app.Dialog; +import android.content.Context; +import android.content.res.Configuration; +import android.text.TextUtils; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.view.ViewParent; + +import com.google.common.base.Strings; + +import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybeanMR1; + +public class Locale { + + public static Locale INSTANCE; + + private static final int[] sDialogButtons = new int[] { android.R.id.button1, android.R.id.button2, android.R.id.button3 }; + private static final char LEFT_TO_RIGHT_MARK = '\u200e'; + private static final char RIGHT_TO_LEFT_MARK = '\u200f'; + + public static java.util.Locale localeFromString(String locale) { + if (Strings.isNullOrEmpty(locale)) { + return null; + } + + String[] split = locale.split("-"); + if (split.length == 1) { + return new java.util.Locale(split[0]); + } else if (split.length == 2) { + return new java.util.Locale(split[0], split[1]); + } + throw new RuntimeException(); + } + + private final java.util.Locale deviceLocale; + private final java.util.Locale appLocale; + private final int deviceDirectionality; + private final int appDirectionality; + private final String override; + + public Locale(java.util.Locale deviceLocale, String override) { + this.deviceLocale = deviceLocale; + this.appLocale = localeFromString(override); + this.override = override; + + deviceDirectionality = TextUtils.getLayoutDirectionFromLocale(deviceLocale); + + if (appLocale != null) { + java.util.Locale.setDefault(appLocale); + appDirectionality = TextUtils.getLayoutDirectionFromLocale(appLocale); + } else { + appDirectionality = deviceDirectionality; + } + } + + public java.util.Locale getLocale() { + return appLocale == null ? deviceLocale : appLocale; + } + + public char getDeviceDirectionalityMark() { + return getDirectionalityMark(deviceDirectionality); + } + + public char getDirectionalityMark() { + return getDirectionalityMark(appDirectionality); + } + + private char getDirectionalityMark(int directionality) { + return directionality == View.LAYOUT_DIRECTION_RTL ? RIGHT_TO_LEFT_MARK : LEFT_TO_RIGHT_MARK; + } + + public String getOverride() { + return override; + } + + public Context createConfigurationContext(Context context) { + return appLocale == null ? context : context.createConfigurationContext(getLocaleConfiguration()); + } + + private Configuration getLocaleConfiguration() { + Configuration configuration = new Configuration(); + configuration.setLocale(appLocale); + return configuration; + } + + public void fixDialogButtonDirectionality(Dialog dialog) { + if (appDirectionality != deviceDirectionality) { + for (int id : sDialogButtons) { + ViewParent parent = dialog.findViewById(id).getParent(); + ((View) parent).setLayoutDirection(appDirectionality); + } + } + } + + public void applyOverrideConfiguration(ContextThemeWrapper wrapper) { + if (appLocale != null && atLeastJellybeanMR1()) { + wrapper.applyOverrideConfiguration(getLocaleConfiguration()); + } + } + + public Locale withOverride(String language) { + return new Locale(deviceLocale, language); + } + + public String getDisplayName() { + java.util.Locale locale = getLocale(); + return locale.getDisplayName(locale); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Locale locale = (Locale) o; + + return override != null ? override.equals(locale.override) : locale.override == null; + } + + @Override + public int hashCode() { + return override != null ? override.hashCode() : 0; + } + + @Override + public String toString() { + return "Locale{" + + "deviceLocale=" + deviceLocale + + ", appLocale=" + appLocale + + ", override='" + override + '\'' + + '}'; + } +} diff --git a/src/main/java/org/tasks/locale/LocalePickerDialog.java b/src/main/java/org/tasks/locale/LocalePickerDialog.java index 0e9328fb0..cea957388 100644 --- a/src/main/java/org/tasks/locale/LocalePickerDialog.java +++ b/src/main/java/org/tasks/locale/LocalePickerDialog.java @@ -6,6 +6,8 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; +import com.google.common.base.Function; + import org.tasks.R; import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.DialogFragmentComponent; @@ -13,15 +15,11 @@ import org.tasks.injection.ForApplication; import org.tasks.injection.InjectingDialogFragment; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.Map; import javax.inject.Inject; -import static org.tasks.locale.LocaleUtils.localeFromString; +import static com.google.common.collect.Lists.transform; public class LocalePickerDialog extends InjectingDialogFragment { @@ -31,29 +29,33 @@ public class LocalePickerDialog extends InjectingDialogFragment { } public interface LocaleSelectionHandler { - void onLocaleSelected(String locale); + void onLocaleSelected(Locale locale); } @Inject DialogBuilder dialogBuilder; @Inject @ForApplication Context context; + @Inject Locale locale; private LocaleSelectionHandler callback; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - final Map translations = new HashMap<>(); - for (String translation : getResources().getStringArray(R.array.localization)) { - Locale locale = localeFromString(translation); - translations.put(locale.getDisplayName(locale), translation); + final List locales = new ArrayList<>(); + for (String override : getResources().getStringArray(R.array.localization)) { + locales.add(locale.withOverride(override)); } - final List display = new ArrayList<>(translations.keySet()); - Collections.sort(display); + final List display = transform(locales, new Function() { + @Override + public String apply(Locale input) { + return input.getDisplayName(); + } + }); return dialogBuilder.newDialog() .setItems(display, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); - callback.onLocaleSelected(translations.get(display.get(i))); + callback.onLocaleSelected(locales.get(i)); } }) .setNegativeButton(android.R.string.cancel, null) diff --git a/src/main/java/org/tasks/locale/LocaleUtils.java b/src/main/java/org/tasks/locale/LocaleUtils.java deleted file mode 100644 index b918c85c6..000000000 --- a/src/main/java/org/tasks/locale/LocaleUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.tasks.locale; - -import android.app.Dialog; -import android.content.Context; -import android.content.res.Configuration; -import android.text.TextUtils; -import android.view.ContextThemeWrapper; -import android.view.View; -import android.view.ViewParent; - -import com.google.common.base.Strings; - -import java.util.Locale; - -import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybeanMR1; - -public class LocaleUtils { - public static Locale localeFromString(String locale) { - if (Strings.isNullOrEmpty(locale)) { - return null; - } - - String[] split = locale.split("-"); - if (split.length == 1) { - return new Locale(split[0]); - } else if (split.length == 2) { - return new Locale(split[0], split[1]); - } - throw new RuntimeException(); - } - - private static final int[] sDialogButtons = new int[] { android.R.id.button1, android.R.id.button2, android.R.id.button3 }; - private static final char LEFT_TO_RIGHT_MARK = '\u200e'; - private static final char RIGHT_TO_LEFT_MARK = '\u200f'; - private static String sLocaleString; - private static Locale sLocale; - - public static Locale getLocale() { - return sLocale == null ? Locale.getDefault() : sLocale; - } - - public static char getDirectionalityMark() { - return TextUtils.getLayoutDirectionFromLocale(getLocale()) == View.LAYOUT_DIRECTION_LTR - ? LEFT_TO_RIGHT_MARK - : RIGHT_TO_LEFT_MARK; - } - - public static String getsLocaleString() { - return sLocaleString; - } - - public static void setLocale(String locale) { - sLocaleString = locale; - sLocale = localeFromString(locale); - if (sLocale != null) { - Locale.setDefault(sLocale); - } - } - - public static void fixDialogButtons(Dialog dialog) { - if (sLocale != null) { - int layoutDirectionFromLocale = TextUtils.getLayoutDirectionFromLocale(sLocale); - for (int id : sDialogButtons) { - ViewParent parent = dialog.findViewById(id).getParent(); - setDirection((View) parent, layoutDirectionFromLocale); - } - } - } - - private static void setDirection(View view, int direction) { - view.setLayoutDirection(direction); - } - - public static void applyOverrideConfiguration(ContextThemeWrapper wrapper) { - if (sLocale != null && atLeastJellybeanMR1()) { - wrapper.applyOverrideConfiguration(getLocaleConfiguration()); - } - } - - public static Context createConfigurationContext(Context context) { - return sLocale == null ? context : context.createConfigurationContext(getLocaleConfiguration()); - } - - private static Configuration getLocaleConfiguration() { - Configuration configuration = new Configuration(); - configuration.setLocale(sLocale); - return configuration; - } -} diff --git a/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java b/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java index cfc7f9085..0ed808237 100644 --- a/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java +++ b/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java @@ -28,8 +28,6 @@ import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; -import org.tasks.locale.LocaleUtils; - /** * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls * to be used with AppCompat. diff --git a/src/main/java/org/tasks/preferences/BaseBasicPreferences.java b/src/main/java/org/tasks/preferences/BaseBasicPreferences.java index 1a611fa31..111946dc9 100644 --- a/src/main/java/org/tasks/preferences/BaseBasicPreferences.java +++ b/src/main/java/org/tasks/preferences/BaseBasicPreferences.java @@ -6,7 +6,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; -import android.text.TextUtils; import com.jakewharton.processphoenix.ProcessPhoenix; import com.todoroo.astrid.activity.TaskListActivity; @@ -20,20 +19,17 @@ import org.tasks.analytics.Tracking; import org.tasks.dialogs.DialogBuilder; import org.tasks.dialogs.ThemePickerDialog; import org.tasks.injection.InjectingPreferenceActivity; +import org.tasks.locale.Locale; import org.tasks.locale.LocalePickerDialog; -import org.tasks.locale.LocaleUtils; import org.tasks.themes.ThemeAccent; import org.tasks.themes.ThemeBase; import org.tasks.themes.ThemeColor; -import java.util.Locale; - import javax.inject.Inject; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybeanMR1; import static org.tasks.dialogs.ThemePickerDialog.newThemePickerDialog; import static org.tasks.locale.LocalePickerDialog.newLocalePickerDialog; -import static org.tasks.locale.LocaleUtils.localeFromString; public abstract class BaseBasicPreferences extends InjectingPreferenceActivity implements ThemePickerDialog.ThemePickerCallback, @@ -52,6 +48,7 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i @Inject ThemeColor themeColor; @Inject ThemeAccent themeAccent; @Inject DialogBuilder dialogBuilder; + @Inject Locale locale; private Bundle result; @Override @@ -179,16 +176,16 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i } @Override - public void onLocaleSelected(String newValue) { + public void onLocaleSelected(Locale newValue) { if (newValue == null) { preferences.remove(R.string.p_language); } else { - preferences.setString(R.string.p_language, newValue); + String override = newValue.getOverride(); + preferences.setString(R.string.p_language, override); + tracker.reportEvent(Tracking.Events.SET_PREFERENCE, R.string.p_language, override); } - tracker.reportEvent(Tracking.Events.SET_PREFERENCE, R.string.p_language, newValue); updateLocale(); - String currentValue = LocaleUtils.getsLocaleString(); - if (!TextUtils.equals(currentValue, newValue)) { + if (!locale.equals(newValue)) { dialogBuilder.newDialog() .setMessage(R.string.restart_required) .setPositiveButton(R.string.restart_now, new DialogInterface.OnClickListener() { @@ -207,10 +204,7 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i private void updateLocale() { Preference languagePreference = findPreference(getString(R.string.p_language)); String preference = preferences.getStringValue(R.string.p_language); - Locale locale = localeFromString(preference); - languagePreference.setSummary(locale == null - ? getString(R.string.default_value) - : locale.getDisplayName(locale)); + languagePreference.setSummary(locale.withOverride(preference).getDisplayName()); } @Override diff --git a/src/main/java/org/tasks/time/DateTime.java b/src/main/java/org/tasks/time/DateTime.java index 774dcd3d9..412dfa41d 100644 --- a/src/main/java/org/tasks/time/DateTime.java +++ b/src/main/java/org/tasks/time/DateTime.java @@ -1,11 +1,10 @@ package org.tasks.time; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; -import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -276,7 +275,7 @@ public class DateTime { public String toString(String format) { Calendar calendar = getCalendar(); - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format, LocaleUtils.getLocale()); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format, Locale.INSTANCE.getLocale()); simpleDateFormat.setCalendar(calendar); return simpleDateFormat.format(calendar.getTime()); } diff --git a/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java b/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java index dda885cee..c8002c24f 100644 --- a/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java +++ b/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java @@ -10,7 +10,7 @@ import com.todoroo.astrid.subtasks.SubtasksHelper; import org.tasks.injection.InjectingRemoteViewsService; import org.tasks.injection.ServiceComponent; -import org.tasks.locale.LocaleUtils; +import org.tasks.locale.Locale; import org.tasks.preferences.DefaultFilterProvider; import org.tasks.preferences.Preferences; import org.tasks.themes.ThemeBase; @@ -30,6 +30,7 @@ public class ScrollableWidgetUpdateService extends InjectingRemoteViewsService { @Inject DefaultFilterProvider defaultFilterProvider; @Inject WidgetCheckBoxes widgetCheckBoxes; @Inject ThemeCache themeCache; + @Inject Locale locale; @Override public void onStart(Intent intent, int startId) { @@ -52,7 +53,7 @@ public class ScrollableWidgetUpdateService extends InjectingRemoteViewsService { String filterId = (String) extras.get(FILTER_ID); int widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); ThemeBase themeBase = themeCache.getThemeBase(preferences.getInt(WidgetConfigActivity.PREF_THEME + widgetId, 0)); - return new ScrollableViewsFactory(subtasksHelper, preferences, LocaleUtils.createConfigurationContext(getApplicationContext()), filterId, + return new ScrollableViewsFactory(subtasksHelper, preferences, locale.createConfigurationContext(getApplicationContext()), filterId, themeBase.getTextColor(), widgetId, database, taskService, defaultFilterProvider, widgetCheckBoxes); }