From d18f35965ef91bd10525dd34b032abf0a464b9de Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Sun, 10 Jul 2016 23:10:55 -0500 Subject: [PATCH] Add language preference --- build.gradle | 1 + src/debug/java/org/tasks/BuildSetup.java | 2 +- .../todoroo/astrid/adapter/TaskAdapter.java | 106 ++++++++---------- .../astrid/core/BuiltInFilterExposer.java | 1 - .../org/tasks/dialogs/AlertDialogBuilder.java | 2 +- .../tasks/injection/ApplicationModule.java | 9 +- .../BaseDialogFragmentComponent.java | 3 + .../injection/InjectingAppCompatActivity.java | 5 + .../InjectingPreferenceActivity.java | 18 ++- .../java/org/tasks/intents/TaskIntents.java | 1 - .../org/tasks/locale/LocalePickerDialog.java | 81 +++++++++++++ .../java/org/tasks/locale/LocaleUtils.java | 62 ++++++++++ .../AppCompatPreferenceActivity.java | 2 + .../preferences/BaseBasicPreferences.java | 69 +++++++++++- .../org/tasks/preferences/Preferences.java | 6 + src/main/java/org/tasks/themes/Theme.java | 2 +- src/main/java/org/tasks/themes/ThemeBase.java | 2 +- src/main/java/org/tasks/time/DateTime.java | 5 +- .../widget/ScrollableWidgetUpdateService.java | 3 +- src/main/res/values/arrays.xml | 34 ++++++ src/main/res/values/keys.xml | 2 +- src/main/res/values/strings.xml | 5 + src/main/res/xml/preferences.xml | 32 ++++-- 23 files changed, 372 insertions(+), 81 deletions(-) create mode 100644 src/main/java/org/tasks/locale/LocalePickerDialog.java create mode 100644 src/main/java/org/tasks/locale/LocaleUtils.java diff --git a/build.gradle b/build.gradle index 984b1e0d6..00738a874 100644 --- a/build.gradle +++ b/build.gradle @@ -127,6 +127,7 @@ dependencies { compile "com.android.support:support-v13:${SUPPORT_VERSION}" compile 'com.jakewharton.timber:timber:4.1.2' compile 'com.google.guava:guava:19.0' + compile 'com.jakewharton:process-phoenix:1.0.2' compile ('com.rubiconproject.oss:jchronic:0.2.6') { transitive = false } diff --git a/src/debug/java/org/tasks/BuildSetup.java b/src/debug/java/org/tasks/BuildSetup.java index 331d5fbbf..2b166c2c3 100644 --- a/src/debug/java/org/tasks/BuildSetup.java +++ b/src/debug/java/org/tasks/BuildSetup.java @@ -25,6 +25,6 @@ public class BuildSetup { Timber.plant(new Timber.DebugTree()); Timber.plant(new StethoTree()); Stetho.initializeWithDefaults(context); - LeakCanary.install((Application) context); + LeakCanary.install((Application) context.getApplicationContext()); } } diff --git a/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java b/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java index 0bd7a919e..7e0f687d0 100644 --- a/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/src/main/java/com/todoroo/astrid/adapter/TaskAdapter.java @@ -5,7 +5,6 @@ */ package com.todoroo.astrid.adapter; -import android.app.Activity; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.content.res.Resources; @@ -163,7 +162,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable { this.themeCache = themeCache; this.resources = fragment.getResources(); this.onCompletedTaskListener = onCompletedTaskListener; - inflater = (LayoutInflater) fragment.getActivity().getSystemService( + inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); TypedValue typedValue = new TypedValue(); @@ -475,11 +474,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable { * uncompleted. */ protected void setTaskAppearance(ViewHolder viewHolder, Task task) { - Activity activity = fragment.getActivity(); - if (activity == null) { - return; - } - boolean state = task.isCompleted(); TextView name = viewHolder.nameView; @@ -501,7 +495,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable { } setupCompleteBox(viewHolder); - } private void setupCompleteBox(ViewHolder viewHolder) { @@ -570,62 +563,59 @@ public class TaskAdapter extends CursorAdapter implements Filterable { private void setupDueDateAndTags(ViewHolder viewHolder, Task task) { // due date / completion date final TextView dueDateView = viewHolder.dueDate; { - Activity activity = fragment.getActivity(); - if (activity != null) { - if(!task.isCompleted() && task.hasDueDate()) { - long dueDate = task.getDueDate(); - if(task.isOverdue()) { - dueDateView.setTextColor(textColorOverdue); - } else { - dueDateView.setTextColor(textColorSecondary); - } - String dateValue = DateUtilities.getRelativeDateStringWithTime(context, dueDate); - dueDateView.setText(dateValue); - dueDateView.setVisibility(View.VISIBLE); - } else if(task.isCompleted()) { - String dateValue = DateUtilities.getRelativeDateStringWithTime(context, task.getCompletionDate()); - dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue)); - dueDateView.setTextColor(textColorHint); - dueDateView.setVisibility(View.VISIBLE); + if(!task.isCompleted() && task.hasDueDate()) { + long dueDate = task.getDueDate(); + if(task.isOverdue()) { + dueDateView.setTextColor(textColorOverdue); } else { - dueDateView.setVisibility(View.GONE); + dueDateView.setTextColor(textColorSecondary); } + String dateValue = DateUtilities.getRelativeDateStringWithTime(context, dueDate); + dueDateView.setText(dateValue); + dueDateView.setVisibility(View.VISIBLE); + } else if(task.isCompleted()) { + String dateValue = DateUtilities.getRelativeDateStringWithTime(context, task.getCompletionDate()); + dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue)); + dueDateView.setTextColor(textColorHint); + dueDateView.setVisibility(View.VISIBLE); + } else { + dueDateView.setVisibility(View.GONE); + } - if (task.isCompleted()) { - viewHolder.tagBlock.setVisibility(View.GONE); - } else { - String tags = viewHolder.tagsString; - List tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList(); - Iterable t = filter(transform(tagUuids, uuidToTag), Predicates.notNull()); - List firstFourByName = orderByName.leastOf(t, 4); - int numTags = firstFourByName.size(); - if (numTags > 0) { - List firstFourByNameLength = orderByLength.sortedCopy(firstFourByName); - float maxLength = tagCharacters / numTags; - for (int i = 0; i < numTags - 1; i++) { - TagData tagData = firstFourByNameLength.get(i); - String name = tagData.getName(); - if (name.length() >= maxLength) { - break; - } - float excess = maxLength - name.length(); - int beneficiaries = numTags - i - 1; - float additional = excess / beneficiaries; - maxLength += additional; + if (task.isCompleted()) { + viewHolder.tagBlock.setVisibility(View.GONE); + } else { + String tags = viewHolder.tagsString; + List tagUuids = tags != null ? newArrayList(tags.split(",")) : Lists.newArrayList(); + Iterable t = filter(transform(tagUuids, uuidToTag), Predicates.notNull()); + List firstFourByName = orderByName.leastOf(t, 4); + int numTags = firstFourByName.size(); + if (numTags > 0) { + List firstFourByNameLength = orderByLength.sortedCopy(firstFourByName); + float maxLength = tagCharacters / numTags; + for (int i = 0; i < numTags - 1; i++) { + TagData tagData = firstFourByNameLength.get(i); + String name = tagData.getName(); + if (name.length() >= maxLength) { + break; } - List tagStrings = transform(firstFourByName, tagToString(maxLength)); - SpannableStringBuilder builder = new SpannableStringBuilder(); - for (SpannableString tagString : tagStrings) { - if (builder.length() > 0) { - builder.append(HAIR_SPACE); - } - builder.append(tagString); + float excess = maxLength - name.length(); + int beneficiaries = numTags - i - 1; + float additional = excess / beneficiaries; + maxLength += additional; + } + List tagStrings = transform(firstFourByName, tagToString(maxLength)); + SpannableStringBuilder builder = new SpannableStringBuilder(); + for (SpannableString tagString : tagStrings) { + if (builder.length() > 0) { + builder.append(HAIR_SPACE); } - viewHolder.tagBlock.setText(builder); - viewHolder.tagBlock.setVisibility(View.VISIBLE); - } else { - viewHolder.tagBlock.setVisibility(View.GONE); + builder.append(tagString); } + viewHolder.tagBlock.setText(builder); + viewHolder.tagBlock.setVisibility(View.VISIBLE); + } else { + viewHolder.tagBlock.setVisibility(View.GONE); } } } diff --git a/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java b/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java index fa3682d46..2ea100992 100644 --- a/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java +++ b/src/main/java/com/todoroo/astrid/core/BuiltInFilterExposer.java @@ -17,7 +17,6 @@ import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.dao.MetadataDao; -import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; diff --git a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java index 0ac1cadd2..7252edb6f 100644 --- a/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java +++ b/src/main/java/org/tasks/dialogs/AlertDialogBuilder.java @@ -3,7 +3,7 @@ package org.tasks.dialogs; import android.content.Context; import android.content.DialogInterface; import android.support.v7.app.AlertDialog; -import android.support.v7.view.ContextThemeWrapper; +import android.view.ContextThemeWrapper; import android.view.View; import android.widget.ListAdapter; diff --git a/src/main/java/org/tasks/injection/ApplicationModule.java b/src/main/java/org/tasks/injection/ApplicationModule.java index cfe00352d..72df80fee 100644 --- a/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/src/main/java/org/tasks/injection/ApplicationModule.java @@ -1,9 +1,13 @@ 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.LocaleUtils; import org.tasks.preferences.Preferences; import org.tasks.themes.ThemeAccent; import org.tasks.themes.ThemeBase; @@ -28,7 +32,10 @@ public class ApplicationModule { private Context context; public ApplicationModule(Context context) { - this.context = context.getApplicationContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String language = prefs.getString(context.getString(R.string.p_language), null); + LocaleUtils.setLocale(language); + this.context = LocaleUtils.withLocale(context.getApplicationContext()); } @Provides diff --git a/src/main/java/org/tasks/injection/BaseDialogFragmentComponent.java b/src/main/java/org/tasks/injection/BaseDialogFragmentComponent.java index 8664aa10b..7aa1b3699 100644 --- a/src/main/java/org/tasks/injection/BaseDialogFragmentComponent.java +++ b/src/main/java/org/tasks/injection/BaseDialogFragmentComponent.java @@ -3,6 +3,7 @@ package org.tasks.injection; import org.tasks.activities.CalendarSelectionDialog; import org.tasks.dialogs.AccountSelectionDialog; import org.tasks.dialogs.AddAttachmentDialog; +import org.tasks.locale.LocalePickerDialog; import org.tasks.dialogs.SortDialog; import org.tasks.dialogs.ThemePickerDialog; import org.tasks.reminders.MissedCallDialog; @@ -28,4 +29,6 @@ public interface BaseDialogFragmentComponent { void inject(ThemePickerDialog themePickerDialog); void inject(SortDialog sortDialog); + + void inject(LocalePickerDialog localePickerDialog); } diff --git a/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java b/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java index 917c10353..cde7e4f27 100644 --- a/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java +++ b/src/main/java/org/tasks/injection/InjectingAppCompatActivity.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import org.tasks.analytics.Tracker; +import org.tasks.locale.LocaleUtils; import javax.inject.Inject; @@ -12,6 +13,10 @@ public abstract class InjectingAppCompatActivity extends AppCompatActivity imple @Inject Tracker tracker; + public InjectingAppCompatActivity() { + LocaleUtils.updateConfig(this); + } + @Override protected void onCreate(Bundle savedInstanceState) { activityComponent = ((InjectingApplication) getApplication()).getComponent().plus(new ActivityModule(this)); diff --git a/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java b/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java index cbd1d27ce..207d45dea 100644 --- a/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java +++ b/src/main/java/org/tasks/injection/InjectingPreferenceActivity.java @@ -1,5 +1,7 @@ package org.tasks.injection; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceCategory; @@ -11,12 +13,15 @@ import android.widget.LinearLayout; import org.tasks.R; import org.tasks.analytics.Tracker; +import org.tasks.locale.LocaleUtils; import org.tasks.preferences.AppCompatPreferenceActivity; import org.tasks.themes.Theme; import org.tasks.ui.MenuColorizer; import javax.inject.Inject; +import timber.log.Timber; + public abstract class InjectingPreferenceActivity extends AppCompatPreferenceActivity implements InjectingActivity { private ActivityComponent activityComponent; @@ -26,6 +31,10 @@ public abstract class InjectingPreferenceActivity extends AppCompatPreferenceAct @Inject Theme theme; @Inject Tracker tracker; + public InjectingPreferenceActivity() { + LocaleUtils.updateConfig(this); + } + @Override public void onCreate(Bundle savedInstanceState) { activityComponent = ((InjectingApplication) getApplication()) @@ -46,7 +55,14 @@ public abstract class InjectingPreferenceActivity extends AppCompatPreferenceAct root.addView(toolbarContainer); toolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar); - toolbar.setTitle(getTitle()); + try { + ComponentName componentName = new ComponentName(this, getClass()); + ActivityInfo activityInfo = getPackageManager().getActivityInfo(componentName, 0); + toolbar.setTitle(activityInfo.labelRes); + } catch (Exception e) { + Timber.e(e, e.getMessage()); + toolbar.setTitle(getTitle()); + } toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_24dp)); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override diff --git a/src/main/java/org/tasks/intents/TaskIntents.java b/src/main/java/org/tasks/intents/TaskIntents.java index f81585f48..ebe2febd4 100644 --- a/src/main/java/org/tasks/intents/TaskIntents.java +++ b/src/main/java/org/tasks/intents/TaskIntents.java @@ -6,7 +6,6 @@ import android.support.v4.app.TaskStackBuilder; import com.google.common.base.Strings; import com.todoroo.astrid.activity.TaskListActivity; -import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.api.Filter; public class TaskIntents { diff --git a/src/main/java/org/tasks/locale/LocalePickerDialog.java b/src/main/java/org/tasks/locale/LocalePickerDialog.java new file mode 100644 index 000000000..527e07322 --- /dev/null +++ b/src/main/java/org/tasks/locale/LocalePickerDialog.java @@ -0,0 +1,81 @@ +package org.tasks.locale; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + +import org.tasks.R; +import org.tasks.dialogs.DialogBuilder; +import org.tasks.injection.DialogFragmentComponent; +import org.tasks.injection.ForApplication; +import org.tasks.injection.InjectingDialogFragment; + +import java.util.Arrays; +import java.util.List; + +import javax.inject.Inject; + +import static com.google.common.collect.Iterables.toArray; +import static org.tasks.locale.LocaleUtils.localeFromString; + + +public class LocalePickerDialog extends InjectingDialogFragment { + + public static LocalePickerDialog newLocalePickerDialog() { + return new LocalePickerDialog(); + } + + public interface LocaleSelectionHandler { + void onLocaleSelected(String locale); + } + + @Inject DialogBuilder dialogBuilder; + @Inject @ForApplication Context context; + + private LocaleSelectionHandler callback; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final String[] translations = context.getResources().getStringArray(R.array.localization); + final List display = Lists.transform(Arrays.asList(translations), new Function() { + @Override + public String apply(String locale) { + return localeFromString(locale).getDisplayName(); + } + }); + return dialogBuilder.newDialog() + .setItems(toArray(display, String.class), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.dismiss(); + callback.onLocaleSelected(translations[i]); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .setNeutralButton(R.string.default_value, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.dismiss(); + callback.onLocaleSelected(null); + } + }) + .show(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + callback = (LocaleSelectionHandler) activity; + } + + @Override + protected void inject(DialogFragmentComponent component) { + component.inject(this); + } +} diff --git a/src/main/java/org/tasks/locale/LocaleUtils.java b/src/main/java/org/tasks/locale/LocaleUtils.java new file mode 100644 index 000000000..75fabb137 --- /dev/null +++ b/src/main/java/org/tasks/locale/LocaleUtils.java @@ -0,0 +1,62 @@ +package org.tasks.locale; + +import android.content.Context; +import android.content.res.Configuration; +import android.view.ContextThemeWrapper; + +import com.google.api.client.repackaged.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 String sLocaleString; + private static Locale sLocale; + + public static Locale getLocale() { + return sLocale == null ? Locale.getDefault() : sLocale; + } + + 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 updateConfig(ContextThemeWrapper wrapper) { + if (sLocale != null && atLeastJellybeanMR1()) { + wrapper.applyOverrideConfiguration(getLocaleConfiguration()); + } + } + + public static Context withLocale(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 0ed808237..cfc7f9085 100644 --- a/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java +++ b/src/main/java/org/tasks/preferences/AppCompatPreferenceActivity.java @@ -28,6 +28,8 @@ 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 b3f117f71..6cd66cf1d 100644 --- a/src/main/java/org/tasks/preferences/BaseBasicPreferences.java +++ b/src/main/java/org/tasks/preferences/BaseBasicPreferences.java @@ -2,31 +2,49 @@ package org.tasks.preferences; import android.app.Activity; import android.app.FragmentManager; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; +import android.text.TextUtils; +import com.google.common.base.Strings; +import com.jakewharton.processphoenix.ProcessPhoenix; +import com.todoroo.astrid.activity.TaskListActivity; +import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.core.OldTaskPreferences; import com.todoroo.astrid.reminders.ReminderPreferences; import org.tasks.R; import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracking; +import org.tasks.dialogs.DialogBuilder; +import org.tasks.locale.LocalePickerDialog; import org.tasks.dialogs.ThemePickerDialog; import org.tasks.injection.InjectingPreferenceActivity; +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.locale.LocalePickerDialog.newLocalePickerDialog; import static org.tasks.dialogs.ThemePickerDialog.newThemePickerDialog; +import static org.tasks.locale.LocaleUtils.localeFromString; -public abstract class BaseBasicPreferences extends InjectingPreferenceActivity implements ThemePickerDialog.ThemePickerCallback { +public abstract class BaseBasicPreferences extends InjectingPreferenceActivity implements + ThemePickerDialog.ThemePickerCallback, + LocalePickerDialog.LocaleSelectionHandler { private static final String EXTRA_RESULT = "extra_result"; private static final String FRAG_TAG_THEME_PICKER = "frag_tag_theme_picker"; + private static final String FRAG_TAG_COLOR_PICKER = "frag_tag_color_picker"; private static final String FRAG_TAG_ACCENT_PICKER = "frag_tag_accent_picker"; + private static final String FRAG_TAG_LOCALE_PICKER = "frag_tag_locale_picker"; private static final int RC_PREFS = 10001; @Inject Tracker tracker; @@ -34,6 +52,7 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i @Inject ThemeBase themeBase; @Inject ThemeColor themeColor; @Inject ThemeAccent themeAccent; + @Inject DialogBuilder dialogBuilder; private Bundle result; @Override @@ -67,7 +86,7 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager.findFragmentByTag(FRAG_TAG_THEME_PICKER) == null) { newThemePickerDialog(ThemePickerDialog.ColorPalette.COLORS) - .show(fragmentManager, FRAG_TAG_THEME_PICKER); + .show(fragmentManager, FRAG_TAG_COLOR_PICKER); } return false; } @@ -85,7 +104,16 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i return false; } }); - + Preference languagePreference = findPreference(getString(R.string.p_language)); + updateLocale(); + languagePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + newLocalePickerDialog() + .show(getFragmentManager(), FRAG_TAG_LOCALE_PICKER); + return false; + } + }); findPreference(getString(R.string.p_collect_statistics)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -100,6 +128,8 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i setupActivity(R.string.EPr_appearance_header, AppearancePreferences.class); setupActivity(R.string.notifications, ReminderPreferences.class); setupActivity(R.string.EPr_manage_header, OldTaskPreferences.class); + + requires(R.string.settings_general, atLeastJellybeanMR1(), R.string.p_language); } private void setupActivity(int key, final Class target) { @@ -149,6 +179,39 @@ public abstract class BaseBasicPreferences extends InjectingPreferenceActivity i recreate(); } + @Override + public void onLocaleSelected(String newValue) { + if (newValue == null) { + preferences.remove(R.string.p_language); + } else { + preferences.setString(R.string.p_language, newValue); + } + updateLocale(); + String currentValue = LocaleUtils.getsLocaleString(); + if (!TextUtils.equals(currentValue, 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(); + } + } + + private void updateLocale() { + Preference languagePreference = findPreference(getString(R.string.p_language)); + String locale = preferences.getStringValue(R.string.p_language); + languagePreference.setSummary(Strings.isNullOrEmpty(locale) + ? getString(R.string.default_value) + : localeFromString(locale).getDisplayName()); + } + @Override public void finish() { setResult(Activity.RESULT_OK, new Intent() {{ diff --git a/src/main/java/org/tasks/preferences/Preferences.java b/src/main/java/org/tasks/preferences/Preferences.java index eb7e7ad74..982b73f3e 100644 --- a/src/main/java/org/tasks/preferences/Preferences.java +++ b/src/main/java/org/tasks/preferences/Preferences.java @@ -369,4 +369,10 @@ public class Preferences { } return Longs.toArray(pattern); } + + public void remove(int resId) { + Editor editor = prefs.edit(); + editor.remove(context.getString(resId)); + editor.apply(); + } } diff --git a/src/main/java/org/tasks/themes/Theme.java b/src/main/java/org/tasks/themes/Theme.java index ae44a8194..0aa28afb3 100644 --- a/src/main/java/org/tasks/themes/Theme.java +++ b/src/main/java/org/tasks/themes/Theme.java @@ -4,7 +4,7 @@ import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; -import android.support.v7.view.ContextThemeWrapper; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import org.tasks.R; diff --git a/src/main/java/org/tasks/themes/ThemeBase.java b/src/main/java/org/tasks/themes/ThemeBase.java index ae47d7f89..e00a3fb8c 100644 --- a/src/main/java/org/tasks/themes/ThemeBase.java +++ b/src/main/java/org/tasks/themes/ThemeBase.java @@ -2,7 +2,7 @@ package org.tasks.themes; import android.app.Activity; import android.content.Context; -import android.support.v7.view.ContextThemeWrapper; +import android.view.ContextThemeWrapper; import org.tasks.R; diff --git a/src/main/java/org/tasks/time/DateTime.java b/src/main/java/org/tasks/time/DateTime.java index 188f64c00..774dcd3d9 100644 --- a/src/main/java/org/tasks/time/DateTime.java +++ b/src/main/java/org/tasks/time/DateTime.java @@ -1,8 +1,11 @@ package org.tasks.time; +import org.tasks.locale.LocaleUtils; + 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; @@ -273,7 +276,7 @@ public class DateTime { public String toString(String format) { Calendar calendar = getCalendar(); - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format, LocaleUtils.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 bc39e4885..679a319c7 100644 --- a/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java +++ b/src/main/java/org/tasks/widget/ScrollableWidgetUpdateService.java @@ -10,6 +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.preferences.DefaultFilterProvider; import org.tasks.preferences.Preferences; import org.tasks.themes.ThemeBase; @@ -51,7 +52,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, this, filterId, + return new ScrollableViewsFactory(subtasksHelper, preferences, LocaleUtils.withLocale(getApplicationContext()), filterId, themeBase.getTextColor(), widgetId, database, taskService, defaultFilterProvider, widgetCheckBoxes); } diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 9521ed9a1..dab46f064 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -129,4 +129,38 @@ @string/theme_deep_orange + + ar + bg-bg + ca + cs + da + de + el + en + es + fa + fi + fr + hu + it + iw + ja + ko + nb + nl + pl + pt + pt-br + ru + sk + sl-si + sv + th + tr + uk + zh-cn + zh-tw + + \ No newline at end of file diff --git a/src/main/res/values/keys.xml b/src/main/res/values/keys.xml index 779a3282b..8936eb32f 100644 --- a/src/main/res/values/keys.xml +++ b/src/main/res/values/keys.xml @@ -305,5 +305,5 @@ theme_accent default_gtasks_list sync_warning_shown - + language diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 4843fa8d7..4f05ac044 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -893,5 +893,10 @@ File %1$s contained %2$s.\n\n every three days every week Automatic synchronization is currently disabled by Android + General + Language + Tasks must be restarted for change to take effect + Restart now + Later diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 20fdd636c..841fe5827 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -1,17 +1,31 @@ - + - + - + + + + + + + + + + +