From ea45c636d32ee8e4804c5a036a0f2afdd8bf44dd Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 14 Jul 2016 08:43:59 -0500 Subject: [PATCH] Add layout direction preference --- .../astrid/ui/MultilineListPreference.java | 2 +- .../org/tasks/dialogs/AlertDialogBuilder.java | 4 +- .../tasks/injection/ApplicationModule.java | 8 +- .../tasks/injection/InjectingApplication.java | 17 +++- .../injection/InjectingContentProvider.java | 2 +- src/main/java/org/tasks/locale/Locale.java | 85 ++++++++++++------- .../org/tasks/locale/LocalePickerDialog.java | 2 +- .../preferences/BaseBasicPreferences.java | 45 ++++++---- .../tasks/widget/ScrollableViewsFactory.java | 10 +++ src/main/res/layout/comment_adapter_row.xml | 2 +- src/main/res/layout/control_set_timers.xml | 1 + .../res/layout/filter_adapter_subheader.xml | 3 + src/main/res/layout/fragment_comment_bar.xml | 1 + .../res/layout/widget_config_activity.xml | 8 +- src/main/res/layout/widget_row.xml | 50 +++++------ src/main/res/values/arrays.xml | 11 +++ src/main/res/values/keys.xml | 1 + src/main/res/values/strings.xml | 4 + src/main/res/values/styles.xml | 1 + src/main/res/xml/preferences.xml | 7 ++ src/main/res/xml/widget_provider_info.xml | 2 +- 21 files changed, 176 insertions(+), 90 deletions(-) diff --git a/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java b/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java index e6a164175..6950ef81c 100644 --- a/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java +++ b/src/main/java/com/todoroo/astrid/ui/MultilineListPreference.java @@ -35,7 +35,7 @@ public class MultilineListPreference extends ListPreference { @Override protected void showDialog(Bundle state) { super.showDialog(state); - Locale.INSTANCE.fixDialogButtonDirectionality(getDialog()); + Locale.INSTANCE.applyDirectionality(getDialog()); } @Override diff --git a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java index bf1dbc796..8d81fb74e 100644 --- a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java +++ b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java @@ -128,14 +128,14 @@ public class AlertDialogBuilder { AlertDialog dialog = create(); theme.applyToContext(dialog.getListView().getContext()); dialog.show(); - locale.fixDialogButtonDirectionality(dialog); + locale.applyDirectionality(dialog); return dialog; } public AlertDialog show() { AlertDialog dialog = create(); dialog.show(); - locale.fixDialogButtonDirectionality(dialog); + locale.applyDirectionality(dialog); return dialog; } } diff --git a/src/main/java/org/tasks/injection/ApplicationModule.java b/src/main/java/org/tasks/injection/ApplicationModule.java index b51c243ea..c71c0bd30 100644 --- a/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/src/main/java/org/tasks/injection/ApplicationModule.java @@ -1,11 +1,8 @@ package org.tasks.injection; import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import org.tasks.ErrorReportingSingleThreadExecutor; -import org.tasks.R; import org.tasks.analytics.Tracker; import org.tasks.locale.Locale; import org.tasks.themes.ThemeCache; @@ -28,10 +25,7 @@ public class ApplicationModule { private Context context; public ApplicationModule(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String language = prefs.getString(context.getString(R.string.p_language), null); - Locale.INSTANCE = new Locale(java.util.Locale.getDefault(), language); - this.context = Locale.INSTANCE.createConfigurationContext(context.getApplicationContext()); + this.context = context; } @Provides diff --git a/src/main/java/org/tasks/injection/InjectingApplication.java b/src/main/java/org/tasks/injection/InjectingApplication.java index 197e56e4a..cee71c0ef 100644 --- a/src/main/java/org/tasks/injection/InjectingApplication.java +++ b/src/main/java/org/tasks/injection/InjectingApplication.java @@ -1,7 +1,13 @@ package org.tasks.injection; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.multidex.MultiDexApplication; +import org.tasks.R; +import org.tasks.locale.Locale; + public abstract class InjectingApplication extends MultiDexApplication { private ApplicationComponent applicationComponent; @@ -10,8 +16,15 @@ public abstract class InjectingApplication extends MultiDexApplication { public void onCreate() { super.onCreate(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String language = prefs.getString(getString(R.string.p_language), null); + int directionOverride = Integer.parseInt(prefs.getString(getString(R.string.p_layout_direction), "-1")); + Locale.INSTANCE = new Locale(java.util.Locale.getDefault(), language, directionOverride); + java.util.Locale.setDefault(Locale.INSTANCE.getLocale()); + Context context = Locale.INSTANCE.createConfigurationContext(getApplicationContext()); + applicationComponent = DaggerApplicationComponent.builder() - .applicationModule(new ApplicationModule(this)) + .applicationModule(new ApplicationModule(context)) .build(); inject(applicationComponent); @@ -22,6 +35,4 @@ public abstract class InjectingApplication extends MultiDexApplication { public ApplicationComponent getComponent() { return applicationComponent; } - - } diff --git a/src/main/java/org/tasks/injection/InjectingContentProvider.java b/src/main/java/org/tasks/injection/InjectingContentProvider.java index 2df4ac686..3cf90ad8e 100644 --- a/src/main/java/org/tasks/injection/InjectingContentProvider.java +++ b/src/main/java/org/tasks/injection/InjectingContentProvider.java @@ -8,7 +8,7 @@ public abstract class InjectingContentProvider extends ContentProvider { public boolean onCreate() { Context context = getContext(); inject(DaggerContentProviderComponent.builder() - .applicationModule(new ApplicationModule(context)) + .applicationModule(new ApplicationModule(context.getApplicationContext())) .contentProviderModule(new ContentProviderModule()) .build()); diff --git a/src/main/java/org/tasks/locale/Locale.java b/src/main/java/org/tasks/locale/Locale.java index c7a190760..b9834975b 100644 --- a/src/main/java/org/tasks/locale/Locale.java +++ b/src/main/java/org/tasks/locale/Locale.java @@ -20,7 +20,7 @@ public class Locale { 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) { + private static java.util.Locale localeFromString(String locale) { if (Strings.isNullOrEmpty(locale)) { return null; } @@ -38,29 +38,36 @@ public class Locale { private final java.util.Locale appLocale; private final int deviceDirectionality; private final int appDirectionality; - private final String override; + private final String languageOverride; + private final int directionOverride; + private final boolean hasUserOverrides; - public Locale(java.util.Locale deviceLocale, String override) { + public Locale(java.util.Locale deviceLocale, String languageOverride, int directionOverride) { this.deviceLocale = deviceLocale; - this.appLocale = localeFromString(override); - this.override = override; - + this.languageOverride = languageOverride; + this.directionOverride = directionOverride; deviceDirectionality = TextUtils.getLayoutDirectionFromLocale(deviceLocale); - if (appLocale != null) { - java.util.Locale.setDefault(appLocale); + java.util.Locale override = localeFromString(languageOverride); + if (override != null) { + appLocale = override; + } else { + appLocale = deviceLocale; + } + + if (directionOverride == View.LAYOUT_DIRECTION_LTR || directionOverride == View.LAYOUT_DIRECTION_RTL) { + appDirectionality = directionOverride; + } else if (appLocale != null) { appDirectionality = TextUtils.getLayoutDirectionFromLocale(appLocale); } else { appDirectionality = deviceDirectionality; } - } - public java.util.Locale getLocale() { - return appLocale == null ? deviceLocale : appLocale; + hasUserOverrides = !(deviceLocale.equals(appLocale) && appDirectionality == deviceDirectionality) && atLeastJellybeanMR1(); } - public char getDeviceDirectionalityMark() { - return getDirectionalityMark(deviceDirectionality); + public java.util.Locale getLocale() { + return appLocale; } public char getDirectionalityMark() { @@ -75,37 +82,37 @@ public class Locale { return appDirectionality; } - public String getOverride() { - return override; + public String getLanguageOverride() { + return languageOverride; } public Context createConfigurationContext(Context context) { - return appLocale == null ? context : context.createConfigurationContext(getLocaleConfiguration()); + return hasUserOverrides + ? context.createConfigurationContext(getLocaleConfiguration()) + : context; } private Configuration getLocaleConfiguration() { Configuration configuration = new Configuration(); - configuration.setLocale(appLocale); + configuration.locale = getLocale(); + final int layoutDirection = 1 + appDirectionality; + configuration.screenLayout = (configuration.screenLayout&~Configuration.SCREENLAYOUT_LAYOUTDIR_MASK)| + (layoutDirection << Configuration.SCREENLAYOUT_LAYOUTDIR_SHIFT); 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()) { + if (hasUserOverrides) { wrapper.applyOverrideConfiguration(getLocaleConfiguration()); } } - public Locale withOverride(String language) { - return new Locale(deviceLocale, language); + public Locale withLanguage(String language) { + return new Locale(deviceLocale, language, directionOverride); + } + + public Locale withDirectionality(int directionality) { + return new Locale(deviceLocale, languageOverride, directionality); } public String getDisplayName() { @@ -120,12 +127,12 @@ public class Locale { Locale locale = (Locale) o; - return override != null ? override.equals(locale.override) : locale.override == null; + return languageOverride != null ? languageOverride.equals(locale.languageOverride) : locale.languageOverride == null; } @Override public int hashCode() { - return override != null ? override.hashCode() : 0; + return languageOverride != null ? languageOverride.hashCode() : 0; } @Override @@ -133,7 +140,21 @@ public class Locale { return "Locale{" + "deviceLocale=" + deviceLocale + ", appLocale=" + appLocale + - ", override='" + override + '\'' + + ", deviceDirectionality=" + deviceDirectionality + + ", appDirectionality=" + appDirectionality + + ", languageOverride='" + languageOverride + '\'' + + ", directionOverride=" + directionOverride + + ", hasUserOverrides=" + hasUserOverrides + '}'; } + + public void applyDirectionality(Dialog dialog) { + if (hasUserOverrides) { + dialog.findViewById(android.R.id.content).setLayoutDirection(appDirectionality); + for (int id : sDialogButtons) { + ViewParent parent = dialog.findViewById(id).getParent(); + ((View) parent).setLayoutDirection(appDirectionality); + } + } + } } diff --git a/src/main/java/org/tasks/locale/LocalePickerDialog.java b/src/main/java/org/tasks/locale/LocalePickerDialog.java index cea957388..fee393d3f 100644 --- a/src/main/java/org/tasks/locale/LocalePickerDialog.java +++ b/src/main/java/org/tasks/locale/LocalePickerDialog.java @@ -42,7 +42,7 @@ public class LocalePickerDialog extends InjectingDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { final List locales = new ArrayList<>(); for (String override : getResources().getStringArray(R.array.localization)) { - locales.add(locale.withOverride(override)); + locales.add(locale.withLanguage(override)); } final List display = transform(locales, new Function() { @Override diff --git a/src/main/java/org/tasks/preferences/BaseBasicPreferences.java b/src/main/java/org/tasks/preferences/BaseBasicPreferences.java index 111946dc9..7f3f3141c 100644 --- a/src/main/java/org/tasks/preferences/BaseBasicPreferences.java +++ b/src/main/java/org/tasks/preferences/BaseBasicPreferences.java @@ -110,6 +110,17 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i return false; } }); + findPreference(getString(R.string.p_layout_direction)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + tracker.reportEvent(Tracking.Events.SET_PREFERENCE, R.string.p_layout_direction, o.toString()); + int newValue = Integer.parseInt((String) o); + if (locale.getDirectionality() != locale.withDirectionality(newValue).getDirectionality()) { + showRestartDialog(); + } + return true; + } + }); findPreference(getString(R.string.p_collect_statistics)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -125,7 +136,7 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i setupActivity(R.string.notifications, ReminderPreferences.class); setupActivity(R.string.EPr_manage_header, OldTaskPreferences.class); - requires(R.string.settings_localization, atLeastJellybeanMR1(), R.string.p_language); + requires(R.string.settings_localization, atLeastJellybeanMR1(), R.string.p_language, R.string.p_layout_direction); } private void setupActivity(int key, final Class target) { @@ -180,31 +191,35 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i if (newValue == null) { preferences.remove(R.string.p_language); } else { - String override = newValue.getOverride(); + String override = newValue.getLanguageOverride(); preferences.setString(R.string.p_language, override); tracker.reportEvent(Tracking.Events.SET_PREFERENCE, R.string.p_language, override); } updateLocale(); if (!locale.equals(newValue)) { - dialogBuilder.newDialog() - .setMessage(R.string.restart_required) - .setPositiveButton(R.string.restart_now, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - ProcessPhoenix.triggerRebirth(BaseBasicPreferences.this, new Intent(BaseBasicPreferences.this, TaskListActivity.class) {{ - putExtra(TaskListActivity.OPEN_FILTER, (Filter) null); - }}); - } - }) - .setNegativeButton(R.string.restart_later, null) - .show(); + showRestartDialog(); } } + private void showRestartDialog() { + dialogBuilder.newDialog() + .setMessage(R.string.restart_required) + .setPositiveButton(R.string.restart_now, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + ProcessPhoenix.triggerRebirth(BaseBasicPreferences.this, new Intent(BaseBasicPreferences.this, TaskListActivity.class) {{ + putExtra(TaskListActivity.OPEN_FILTER, (Filter) null); + }}); + } + }) + .setNegativeButton(R.string.restart_later, null) + .show(); + } + private void updateLocale() { Preference languagePreference = findPreference(getString(R.string.p_language)); String preference = preferences.getStringValue(R.string.p_language); - languagePreference.setSummary(locale.withOverride(preference).getDisplayName()); + languagePreference.setSummary(locale.withLanguage(preference).getDisplayName()); } @Override diff --git a/src/main/java/org/tasks/widget/ScrollableViewsFactory.java b/src/main/java/org/tasks/widget/ScrollableViewsFactory.java index c680d9e32..2fc2ca72b 100644 --- a/src/main/java/org/tasks/widget/ScrollableViewsFactory.java +++ b/src/main/java/org/tasks/widget/ScrollableViewsFactory.java @@ -22,12 +22,15 @@ import com.todoroo.astrid.subtasks.SubtasksHelper; import org.tasks.BuildConfig; import org.tasks.R; +import org.tasks.locale.Locale; import org.tasks.preferences.DefaultFilterProvider; import org.tasks.preferences.Preferences; import org.tasks.ui.WidgetCheckBoxes; import timber.log.Timber; +import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybeanMR1; + public class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory { private final WidgetCheckBoxes checkBoxes; @@ -172,6 +175,10 @@ public class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFac row.setOnClickFillInIntent(R.id.widget_complete_box, completeIntent); } + if (atLeastJellybeanMR1()) { + row.setInt(R.id.widget_row, "setLayoutDirection", Locale.INSTANCE.getDirectionality()); + } + return row; } catch (Exception e) { Timber.e(e, e.getMessage()); @@ -203,6 +210,9 @@ public class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFac AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.scrollable_widget); rv.setTextViewText(R.id.widget_title, filter.listingTitle); + if (atLeastJellybeanMR1()) { + rv.setInt(R.id.widget, "setLayoutDirection", Locale.INSTANCE.getDirectionality()); + } appWidgetManager.partiallyUpdateAppWidget(widgetId, rv); String query = SortHelper.adjustQueryForFlagsAndSort(preferences, filter.getSqlQuery(), sort).replaceAll("LIMIT \\d+", ""); return subtasksHelper.applySubtasksToWidgetFilter(filter, query, filter.listingTitle, 0); diff --git a/src/main/res/layout/comment_adapter_row.xml b/src/main/res/layout/comment_adapter_row.xml index 006b45c4a..aa75d18c4 100644 --- a/src/main/res/layout/comment_adapter_row.xml +++ b/src/main/res/layout/comment_adapter_row.xml @@ -13,7 +13,7 @@ diff --git a/src/main/res/layout/control_set_timers.xml b/src/main/res/layout/control_set_timers.xml index 9b98716c7..f1dfcd4e8 100644 --- a/src/main/res/layout/control_set_timers.xml +++ b/src/main/res/layout/control_set_timers.xml @@ -19,6 +19,7 @@ style="@style/TaskEditTextPrimary" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textAlignment="viewStart" android:text="@string/TEA_timer_controls" /> diff --git a/src/main/res/layout/filter_adapter_subheader.xml b/src/main/res/layout/filter_adapter_subheader.xml index 1b6c8093d..e42e147a7 100644 --- a/src/main/res/layout/filter_adapter_subheader.xml +++ b/src/main/res/layout/filter_adapter_subheader.xml @@ -9,12 +9,15 @@ android:layout_gravity="top" /> @@ -55,6 +56,7 @@ android:layout_toLeftOf="@id/selected_theme" android:layout_toStartOf="@id/selected_theme" android:text="@string/theme" + android:textAlignment="viewStart" android:textColor="?attr/asTextColor" android:textSize="18sp" /> @@ -81,6 +83,7 @@ android:layout_toLeftOf="@id/selected_color" android:layout_toStartOf="@id/selected_color" android:text="@string/color" + android:textAlignment="viewStart" android:textColor="?attr/asTextColor" android:textSize="18sp" /> @@ -105,6 +108,7 @@ android:layout_toLeftOf="@id/opacity_value" android:layout_toStartOf="@id/opacity_value" android:text="@string/opacity" + android:textAlignment="viewStart" android:textColor="?attr/asTextColor" android:textSize="18sp" /> @@ -115,8 +119,8 @@ style="@style/WidgetConfigRow" android:layout_width="match_parent" android:layout_height="wrap_content" - android:max="255" - android:indeterminate="false" /> + android:indeterminate="false" + android:max="255" /> - - - - - + android:paddingEnd="5dp" + android:paddingLeft="0dp" + android:paddingRight="5dp" + android:paddingStart="0dp" + android:singleLine="true" + android:textAlignment="viewStart" + android:textSize="16sp" /> - + \ No newline at end of file diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index dab46f064..9f40b1307 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -163,4 +163,15 @@ zh-tw + + @string/layout_direction_locale + @string/layout_direction_left_to_right + @string/layout_direction_right_to_left + + + + -1 + 0 + 1 + \ No newline at end of file diff --git a/src/main/res/values/keys.xml b/src/main/res/values/keys.xml index 8936eb32f..9c2c65249 100644 --- a/src/main/res/values/keys.xml +++ b/src/main/res/values/keys.xml @@ -306,4 +306,5 @@ default_gtasks_list sync_warning_shown language + layout_direction diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d0e839b81..f6b3ee23e 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -899,5 +899,9 @@ File %1$s contained %2$s.\n\n Restart now Later Localization + Layout direction + Use locale direction + Left to right + Right to left diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 1c7aa0c67..7fa6f3efa 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -76,6 +76,7 @@ @null ?android:attr/listChoiceIndicatorMultiple ?android:attr/listChoiceIndicatorMultiple + viewStart diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index d280c68c1..c4338e3a5 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -73,6 +73,13 @@ android:key="@string/p_language" android:title="@string/language" /> + +