diff --git a/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java index 62aa5de73..7f99d7923 100644 --- a/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/amazon/java/org/tasks/injection/DialogFragmentComponent.java @@ -8,6 +8,7 @@ import org.tasks.dialogs.SortDialog; import org.tasks.reminders.MissedCallDialog; import org.tasks.reminders.NotificationDialog; import org.tasks.reminders.SnoozeDialog; +import org.tasks.repeats.CustomRecurrenceDialog; import dagger.Subcomponent; @@ -28,4 +29,6 @@ public interface DialogFragmentComponent { void inject(ColorPickerDialog colorPickerDialog); void inject(RecordAudioDialog recordAudioDialog); + + void inject(CustomRecurrenceDialog customRecurrenceDialog); } diff --git a/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java index 62aa5de73..7f99d7923 100644 --- a/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/generic/java/org/tasks/injection/DialogFragmentComponent.java @@ -8,6 +8,7 @@ import org.tasks.dialogs.SortDialog; import org.tasks.reminders.MissedCallDialog; import org.tasks.reminders.NotificationDialog; import org.tasks.reminders.SnoozeDialog; +import org.tasks.repeats.CustomRecurrenceDialog; import dagger.Subcomponent; @@ -28,4 +29,6 @@ public interface DialogFragmentComponent { void inject(ColorPickerDialog colorPickerDialog); void inject(RecordAudioDialog recordAudioDialog); + + void inject(CustomRecurrenceDialog customRecurrenceDialog); } diff --git a/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java b/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java index 862d108ce..acfae77f2 100644 --- a/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java +++ b/app/src/googleplay/java/org/tasks/injection/DialogFragmentComponent.java @@ -12,6 +12,7 @@ import org.tasks.gtasks.RenameListDialog; import org.tasks.reminders.MissedCallDialog; import org.tasks.reminders.NotificationDialog; import org.tasks.reminders.SnoozeDialog; +import org.tasks.repeats.CustomRecurrenceDialog; import dagger.Subcomponent; @@ -41,4 +42,6 @@ public interface DialogFragmentComponent { void inject(DeleteListDialog deleteListDialog); void inject(RenameListDialog renameListDialog); + + void inject(CustomRecurrenceDialog customRecurrenceDialog); } diff --git a/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CRectFEvaluator.java b/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CRectFEvaluator.java new file mode 100644 index 000000000..468d295b7 --- /dev/null +++ b/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CRectFEvaluator.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright 2015 Vikram Kakkar + * + * 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. + */ + +package com.appeaser.sublimepickerlibrary.drawables; + +import android.animation.TypeEvaluator; +import android.graphics.RectF; + +/** + * This evaluator can be used to perform type interpolation between RectF values. + * It is a modified version of 'RectEvaluator' + */ +public class CRectFEvaluator implements TypeEvaluator { + + /** + * When null, a new Rect is returned on every evaluate call. When non-null, + * mRect will be modified and returned on every evaluate. + */ + private RectF mRectF; + + /** + * Construct a RectEvaluator that returns a new Rect on every evaluate call. + * To avoid creating an object for each evaluate call, + * {@link CRectFEvaluator#CRectFEvaluator(RectF)} should be used + * whenever possible. + */ + public CRectFEvaluator() { + } + + /** + * Constructs a RectEvaluator that modifies and returns reuseRect + * in #evaluate(float, android.graphics.RectF, android.graphics.Rect) calls. + * The value returned from + * #evaluate(float, android.graphics.RectF, android.graphics.Rect) should + * not be cached because it will change over time as the object is reused on each + * call. + * + * @param reuseRect A Rect to be modified and returned by evaluate. + */ + public CRectFEvaluator(RectF reuseRect) { + mRectF = reuseRect; + } + + /** + * This function returns the result of linearly interpolating the start and + * end Rect values, with fraction representing the proportion + * between the start and end values. The calculation is a simple parametric + * calculation on each of the separate components in the Rect objects + * (left, top, right, and bottom). + *

If #CRectFEvaluator(android.graphics.Rect) was used to construct + * this RectEvaluator, the object returned will be the reuseRect + * passed into the constructor.

+ * + * @param fraction The fraction from the starting to the ending values + * @param startValue The start Rect + * @param endValue The end Rect + * @return A linear interpolation between the start and end values, given the + * fraction parameter. + */ + @Override + public RectF evaluate(float fraction, RectF startValue, RectF endValue) { + float left = startValue.left + (endValue.left - startValue.left) * fraction; + float top = startValue.top + (endValue.top - startValue.top) * fraction; + float right = startValue.right + (endValue.right - startValue.right) * fraction; + float bottom = startValue.bottom + (endValue.bottom - startValue.bottom) * fraction; + if (mRectF == null) { + return new RectF(left, top, right, bottom); + } else { + mRectF.set(left, top, right, bottom); + return mRectF; + } + } +} diff --git a/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CheckableDrawable.java b/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CheckableDrawable.java new file mode 100644 index 000000000..93ef9f48c --- /dev/null +++ b/app/src/main/java/com/appeaser/sublimepickerlibrary/drawables/CheckableDrawable.java @@ -0,0 +1,225 @@ +/* + * Copyright 2015 Vikram Kakkar + * + * 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. + */ + +package com.appeaser.sublimepickerlibrary.drawables; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.view.animation.AnticipateInterpolator; +import android.view.animation.OvershootInterpolator; + +/** + * Provides animated transition between 'on' and 'off' state. + * Used as background for 'WeekButton'. + */ +public class CheckableDrawable extends Drawable { + + private final int ANIMATION_DURATION_EXPAND = 500, ANIMATION_DURATION_COLLAPSE = 400; + private int mMinAlpha, mMaxAlpha; + private Paint mPaint; + + private AnimatorSet asTransition; + private final OvershootInterpolator mExpandInterpolator = new OvershootInterpolator(); + private final AnticipateInterpolator mCollapseInterpolator = new AnticipateInterpolator(); + private final CRectFEvaluator mRectEvaluator = new CRectFEvaluator(); + + private RectF mRectToDraw, mCollapsedRect, mExpandedRect; + private int mExpandedWidthHeight; + + private boolean mChecked, mReady; + + public CheckableDrawable(int color, boolean checked, int expandedWidthHeight) { + mChecked = checked; + mExpandedWidthHeight = expandedWidthHeight; + + mMaxAlpha = Color.alpha(color); + // Todo: Provide an option to change this value + mMinAlpha = 0; + + mRectToDraw = new RectF(); + mExpandedRect = new RectF(); + mCollapsedRect = new RectF(); + mPaint = new Paint(); + mPaint.setColor(color); + mPaint.setAlpha(mMaxAlpha); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.FILL); + } + + // initialize dimensions + private void setDimens(int width, int height) { + mReady = true; + + float expandedLeft = (width - mExpandedWidthHeight) / 2f; + float expandedTop = (height - mExpandedWidthHeight) / 2f; + float expandedRight = (width + mExpandedWidthHeight) / 2f; + float expandedBottom = (height + mExpandedWidthHeight) / 2f; + + float collapsedLeft = width / 2f; + float collapsedTop = height / 2f; + float collapsedRight = width / 2f; + float collapsedBottom = height / 2f; + + mCollapsedRect = new RectF(collapsedLeft, collapsedTop, + collapsedRight, collapsedBottom); + mExpandedRect = new RectF(expandedLeft, expandedTop, + expandedRight, expandedBottom); + + reset(); + } + + // Called when 'WeekButton' checked state changes + public void setCheckedOnClick(boolean checked, final OnAnimationDone callback) { + mChecked = checked; + if (!mReady) { + invalidateSelf(); + return; + } + reset(); + onClick(callback); + } + + private void onClick(final OnAnimationDone callback) { + animate(mChecked, callback); + } + + private void cancelAnimationInTracks() { + if (asTransition != null && asTransition.isRunning()) { + asTransition.cancel(); + } + } + + // Set state without animation + public void setChecked(boolean checked) { + if (mChecked == checked) + return; + + mChecked = checked; + reset(); + } + + private void reset() { + cancelAnimationInTracks(); + + if (mChecked) { + mRectToDraw.set(mExpandedRect); + } else { + mRectToDraw.set(mCollapsedRect); + } + + invalidateSelf(); + } + + // Animate between 'on' & 'off' state + private void animate(boolean expand, final OnAnimationDone callback) { + RectF from = expand ? mCollapsedRect : mExpandedRect; + RectF to = expand ? mExpandedRect : mCollapsedRect; + + mRectToDraw.set(from); + + ObjectAnimator oaTransition = ObjectAnimator.ofObject(this, + "newRectBounds", + mRectEvaluator, from, to); + + int duration = expand ? + ANIMATION_DURATION_EXPAND : + ANIMATION_DURATION_COLLAPSE; + + oaTransition.setDuration(duration); + oaTransition.setInterpolator(expand ? + mExpandInterpolator : + mCollapseInterpolator); + + ObjectAnimator oaAlpha = ObjectAnimator.ofInt(this, + "alpha", + expand ? mMinAlpha : mMaxAlpha, + expand ? mMaxAlpha : mMinAlpha); + oaAlpha.setDuration(duration); + + asTransition = new AnimatorSet(); + asTransition.playTogether(oaTransition, oaAlpha); + + asTransition.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + + if (callback != null) { + callback.animationIsDone(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + + if (callback != null) { + callback.animationHasBeenCancelled(); + } + } + }); + + asTransition.start(); + } + + @Override + public void draw(Canvas canvas) { + if (!mReady) { + setDimens(getBounds().width(), getBounds().height()); + return; + } + + canvas.drawOval(mRectToDraw, mPaint); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + // ObjectAnimator property + @SuppressWarnings("unused") + public void setNewRectBounds(RectF newRectBounds) { + mRectToDraw = newRectBounds; + invalidateSelf(); + } + + // Callback + public interface OnAnimationDone { + void animationIsDone(); + + void animationHasBeenCancelled(); + } +} diff --git a/app/src/main/java/com/appeaser/sublimepickerlibrary/recurrencepicker/WeekButton.java b/app/src/main/java/com/appeaser/sublimepickerlibrary/recurrencepicker/WeekButton.java new file mode 100644 index 000000000..e1de68398 --- /dev/null +++ b/app/src/main/java/com/appeaser/sublimepickerlibrary/recurrencepicker/WeekButton.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright 2015 Vikram Kakkar + * + * 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. + */ + +package com.appeaser.sublimepickerlibrary.recurrencepicker; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ToggleButton; + +import com.appeaser.sublimepickerlibrary.drawables.CheckableDrawable; + +public class WeekButton extends ToggleButton { + + private static int mDefaultTextColor, mCheckedTextColor; + + // Drawable that provides animations between + // 'on' & 'off' states + private CheckableDrawable mDrawable; + + // Flag to disable animation on state change + private boolean noAnimate = false; + + public WeekButton(Context context) { + super(context); + } + + public WeekButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public WeekButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + // Syncs state + private CheckableDrawable.OnAnimationDone mCallback = new CheckableDrawable.OnAnimationDone() { + @Override + public void animationIsDone() { + setTextColor(isChecked() ? mCheckedTextColor : mDefaultTextColor); + mDrawable.setChecked(isChecked()); + } + + @Override + public void animationHasBeenCancelled() { + setTextColor(isChecked() ? mCheckedTextColor : mDefaultTextColor); + mDrawable.setChecked(isChecked()); + } + }; + + // Wrapper for 'setChecked(boolean)' that does not trigger + // state-animation + public void setCheckedNoAnimate(boolean checked) { + noAnimate = true; + setChecked(checked); + noAnimate = false; + } + + @Override + public void setChecked(final boolean checked) { + super.setChecked(checked); + + if (mDrawable != null) { + if (noAnimate) { + mDrawable.setChecked(checked); + setTextColor(isChecked() ? mCheckedTextColor : mDefaultTextColor); + } else { + // Reset text color for animation + // The correct state color will be + // set when animation is done or cancelled + setTextColor(isChecked() ? mCheckedTextColor : mDefaultTextColor); + mDrawable.setCheckedOnClick(isChecked(), mCallback); + } + } + } + + @Override + public void setBackgroundDrawable(Drawable d) { + super.setBackgroundDrawable(d); + + if (d instanceof CheckableDrawable) { + mDrawable = (CheckableDrawable) d; + } else { + // Reset: in case setBackgroundDrawable + // is called more than once + mDrawable = null; + } + } + + // State-dependent text-colors + public static void setStateColors(int defaultColor, int checkedColor) { + mDefaultTextColor = defaultColor; + mCheckedTextColor = checkedColor; + } +} diff --git a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.java b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.java index 4d6c864f0..d81f3f9b6 100644 --- a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.java +++ b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.java @@ -7,23 +7,12 @@ package com.todoroo.astrid.repeats; import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; -import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.Spinner; import android.widget.TextView; import com.google.common.base.Strings; @@ -33,19 +22,19 @@ import com.google.ical.values.Weekday; import com.google.ical.values.WeekdayNum; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.ui.NumberPickerDialog; import org.tasks.R; -import org.tasks.activities.DatePickerActivity; import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.ForActivity; import org.tasks.injection.FragmentComponent; import org.tasks.preferences.Preferences; +import org.tasks.repeats.CustomRecurrenceDialog; import org.tasks.themes.Theme; import org.tasks.time.DateTime; +import org.tasks.ui.SingleCheckedArrayAdapter; import org.tasks.ui.TaskEditControlFragment; -import java.text.DateFormatSymbols; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -57,7 +46,9 @@ import butterknife.OnClick; import timber.log.Timber; import static android.support.v4.content.ContextCompat.getColor; +import static com.google.common.collect.Lists.newArrayList; import static org.tasks.date.DateTimeUtils.newDateTime; +import static org.tasks.repeats.CustomRecurrenceDialog.newCustomRecurrenceDialog; /** * Control Set for managing repeats @@ -65,56 +56,60 @@ import static org.tasks.date.DateTimeUtils.newDateTime; * @author Tim Su * */ -public class RepeatControlSet extends TaskEditControlFragment { +public class RepeatControlSet extends TaskEditControlFragment + implements CustomRecurrenceDialog.CustomRecurrenceCallback { public static final int TAG = R.string.TEA_ctrl_repeat_pref; + private static final String FRAG_TAG_CUSTOM_RECURRENCE = "frag_tag_custom_recurrence"; + + @Override + public void onSelected(int frequency, int interval, long repeatUntilValue, + boolean repeatAfterCompletion, boolean[] isChecked) { + doRepeat = true; + this.interval = interval; + this.frequency = frequency; + this.repeatUntilValue = repeatUntilValue; + this.isChecked = isChecked; + this.repeatAfterCompletion = repeatAfterCompletion; + refreshDisplayView(); + } public interface RepeatChangedListener { void repeatChanged(boolean repeat); } - private static final int REQUEST_PICK_DATE = 505; private static final String EXTRA_RECURRENCE = "extra_recurrence"; private static final String EXTRA_REPEAT_UNTIL = "extra_repeat_until"; private static final String EXTRA_REPEAT_AFTER_COMPLETION = "extra_repeat_after_completion"; // --- spinner constants - private static final int INTERVAL_DAYS = 0; - private static final int INTERVAL_WEEKS = 1; - private static final int INTERVAL_MONTHS = 2; - private static final int INTERVAL_HOURS = 3; - private static final int INTERVAL_MINUTES = 4; - private static final int INTERVAL_YEARS = 5; + public static final int FREQUENCY_MINUTES = 0; + public static final int FREQUENCY_HOURS = 1; + public static final int FREQUENCY_DAYS = 2; + public static final int FREQUENCY_WEEKS = 3; + public static final int FREQUENCY_MONTHS = 4; + public static final int FREQUENCY_YEARS = 5; - private static final int TYPE_DUE_DATE = 0; - private static final int TYPE_COMPLETION_DATE = 1; + public static final int TYPE_DUE_DATE = 0; + public static final int TYPE_COMPLETION_DATE = 1; //private final CheckBox enabled; private boolean doRepeat = false; - private Button value; - private Spinner repeatUntil; @Inject DialogBuilder dialogBuilder; @Inject Preferences preferences; @Inject @ForActivity Context context; @Inject Theme theme; - @BindView(R.id.clear) ImageView clear; @BindView(R.id.display_row_edit) TextView displayView; - private ArrayAdapter repeatUntilAdapter; - private final List repeatUntilOptions = new ArrayList<>(); - private LinearLayout daysOfWeekContainer; - private final Weekday[] weekdays = new Weekday[7]; - private final boolean[] isChecked = new boolean[7]; - private String recurrence; - private int repeatValue; - private int intervalValue; + private int interval; + private int frequency; private long repeatUntilValue; - private View dialogView; - private AlertDialog dialog; + private boolean[] isChecked; + private final Weekday[] weekdays = new Weekday[7]; private RepeatChangedListener callback; @@ -130,137 +125,53 @@ public class RepeatControlSet extends TaskEditControlFragment { repeatAfterCompletion = savedInstanceState.getBoolean(EXTRA_REPEAT_AFTER_COMPLETION); } - dialogView = inflater.inflate(R.layout.control_set_repeat, null); - value = dialogView.findViewById(R.id.repeatValue); - Spinner interval = dialogView.findViewById(R.id.repeatInterval); - ArrayAdapter intervalAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.repeat_interval)); - intervalAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - interval.setAdapter(intervalAdapter); - Spinner type = dialogView.findViewById(R.id.repeatType); - ArrayAdapter typeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, getResources().getStringArray(R.array.repeat_type)); - typeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - type.setAdapter(typeAdapter); - type.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - repeatAfterCompletion = position == TYPE_COMPLETION_DATE; - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - daysOfWeekContainer = dialogView.findViewById(R.id.repeatDayOfWeekContainer); - repeatUntil = dialogView.findViewById(R.id.repeat_until); - repeatUntilAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, repeatUntilOptions); - repeatUntilAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - repeatUntil.setAdapter(repeatUntilAdapter); - // set up days of week - DateFormatSymbols dfs = new DateFormatSymbols(); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); - CompoundButton[] daysOfWeek = new CompoundButton[7]; for(int i = 0; i < 7; i++) { - final int index = i; - CheckBox checkBox = (CheckBox) daysOfWeekContainer.getChildAt(i); - checkBox.setOnCheckedChangeListener((buttonView, isChecked1) -> RepeatControlSet.this.isChecked[index] = isChecked1); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); - checkBox.setText(dfs.getShortWeekdays()[dayOfWeek].substring(0, 1)); - daysOfWeek[i] = checkBox; weekdays[i] = Weekday.values()[dayOfWeek - 1]; calendar.add(Calendar.DATE, 1); } - // set up listeners - value.setOnClickListener(v -> repeatValueClick()); - - setRepeatValue(1); - interval.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parentView, View view, int position, long id) { - daysOfWeekContainer.setVisibility(position == INTERVAL_WEEKS ? View.VISIBLE : View.GONE); - intervalValue = position; - } - - @Override - public void onNothingSelected(AdapterView arg0) { - // - } - }); - - setRepeatUntilValue(repeatUntilValue); - repeatUntil.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - if (repeatUntilOptions.size() == 2) { - if (i == 0) { - setRepeatUntilValue(0); - } else { - repeatUntilClick(); - } - } else { - if (i == 1) { - setRepeatUntilValue(0); - } else if (i == 2) { - repeatUntilClick(); - } - } - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - // - } - }); - - daysOfWeekContainer.setVisibility(View.GONE); - type.setSelection(repeatAfterCompletion ? TYPE_COMPLETION_DATE : TYPE_DUE_DATE); doRepeat = !Strings.isNullOrEmpty(recurrence); if (doRepeat) { - // read recurrence rule try { RRule rrule = new RRule(recurrence); - - setRepeatValue(rrule.getInterval()); - - for(WeekdayNum day : rrule.getByDay()) { - for(int i = 0; i < 7; i++) { + interval = rrule.getInterval(); + isChecked = new boolean[7]; + for (WeekdayNum day : rrule.getByDay()) { + for (int i = 0 ; i < 7 ; i++) { if (weekdays[i].equals(day.wday)) { - daysOfWeek[i].setChecked(true); + isChecked[i] = true; } } } - - switch(rrule.getFreq()) { + switch (rrule.getFreq()) { case DAILY: - intervalValue = INTERVAL_DAYS; + frequency = FREQUENCY_DAYS; break; case WEEKLY: - intervalValue = INTERVAL_WEEKS; + frequency = FREQUENCY_WEEKS; break; case MONTHLY: - intervalValue = INTERVAL_MONTHS; + frequency = FREQUENCY_MONTHS; break; case HOURLY: - intervalValue = INTERVAL_HOURS; + frequency = FREQUENCY_HOURS; break; case MINUTELY: - intervalValue = INTERVAL_MINUTES; + frequency = FREQUENCY_MINUTES; break; case YEARLY: - intervalValue = INTERVAL_YEARS; + frequency = FREQUENCY_YEARS; break; - default: - Timber.e(new Exception("Unhandled rrule frequency: " + recurrence), "repeat-unhandled-rule"); - } - interval.setSelection(intervalValue); - } catch (Exception e) { - // invalid RRULE - recurrence = ""; //$NON-NLS-1$ + } + } catch (ParseException e) { + recurrence = ""; Timber.e(e, e.getMessage()); } + } refreshDisplayView(); return view; @@ -287,33 +198,96 @@ public class RepeatControlSet extends TaskEditControlFragment { component.inject(this); } - @OnClick(R.id.clear) - void clearRepeat(View view) { - doRepeat = false; - refreshDisplayView(); - callback.repeatChanged(doRepeat); + private boolean isCustomValue() { + if (!doRepeat) { + return false; + } + if (frequency == FREQUENCY_WEEKS) { + for (boolean checked : isChecked) { + if (checked) { + return true; + } + } + } + return frequency == FREQUENCY_HOURS || + frequency == FREQUENCY_MINUTES || + !(repeatUntilValue == 0 && interval == 1 && !repeatAfterCompletion); } @OnClick(R.id.display_row_edit) void openPopup(View view) { - if (dialog == null) { - dialog = buildDialog(); + boolean customPicked = isCustomValue(); + List repeatOptions = newArrayList(context.getResources().getStringArray(R.array.repeat_options)); + SingleCheckedArrayAdapter adapter = new SingleCheckedArrayAdapter<>(context, repeatOptions); + if (customPicked) { + adapter.insert(getRepeatString(), 0); + adapter.setChecked(0); + } else if (!doRepeat) { + adapter.setChecked(0); + } else { + int selected; + switch(frequency) { + case FREQUENCY_DAYS: + selected = 1; + break; + case FREQUENCY_WEEKS: + selected = 2; + break; + case FREQUENCY_MONTHS: + selected = 3; + break; + case FREQUENCY_YEARS: + selected = 4; + break; + default: + selected = 0; + break; + } + adapter.setChecked(selected); } - dialog.show(); - } - - private AlertDialog buildDialog() { - return dialogBuilder.newDialog() - .setView(dialogView) - .setPositiveButton(android.R.string.ok, (dialog12, which) -> { - doRepeat = true; + dialogBuilder.newDialog() + .setAdapter(adapter, (dialogInterface, i) -> { + if (customPicked) { + if (i == 0) { + return; + } + i--; + } + if (i == 0) { + doRepeat = false; + } else if (i == 5) { + newCustomRecurrenceDialog(this) + .show(getFragmentManager(), FRAG_TAG_CUSTOM_RECURRENCE); + return; + } else { + doRepeat = true; + repeatAfterCompletion = false; + interval = 1; + repeatUntilValue = 0; + + switch (i) { + case 1: + frequency = FREQUENCY_DAYS; + break; + case 2: + frequency = FREQUENCY_WEEKS; + isChecked = new boolean[7]; + break; + case 3: + frequency = FREQUENCY_MONTHS; + break; + case 4: + frequency = FREQUENCY_YEARS; + break; + } + } callback.repeatChanged(doRepeat); refreshDisplayView(); }) - .setOnCancelListener(dialog1 -> refreshDisplayView()) - .create(); + .setOnCancelListener(d -> refreshDisplayView()) + .show(); } @Override @@ -365,12 +339,12 @@ public class RepeatControlSet extends TaskEditControlFragment { result = ""; //$NON-NLS-1$ } else { RRule rrule = new RRule(); - rrule.setInterval(repeatValue); - switch(intervalValue) { - case INTERVAL_DAYS: + rrule.setInterval(interval); + switch(frequency) { + case FREQUENCY_DAYS: rrule.setFreq(Frequency.DAILY); break; - case INTERVAL_WEEKS: { + case FREQUENCY_WEEKS: { rrule.setFreq(Frequency.WEEKLY); ArrayList days = new ArrayList<>(); @@ -382,16 +356,16 @@ public class RepeatControlSet extends TaskEditControlFragment { rrule.setByDay(days); break; } - case INTERVAL_MONTHS: + case FREQUENCY_MONTHS: rrule.setFreq(Frequency.MONTHLY); break; - case INTERVAL_HOURS: + case FREQUENCY_HOURS: rrule.setFreq(Frequency.HOURLY); break; - case INTERVAL_MINUTES: + case FREQUENCY_MINUTES: rrule.setFreq(Frequency.MINUTELY); break; - case INTERVAL_YEARS: + case FREQUENCY_YEARS: rrule.setFreq(Frequency.YEARLY); break; } @@ -402,65 +376,35 @@ public class RepeatControlSet extends TaskEditControlFragment { return result; } - /** Set up the repeat value button */ - private void setRepeatValue(int newValue) { - repeatValue = newValue; - value.setText(getString(R.string.repeat_every, newValue)); - } - - private void setRepeatUntilValue(long newValue) { - repeatUntilValue = newValue; - updateRepeatUntilOptions(); - } - - private void repeatValueClick() { - int dialogValue = repeatValue; - if(dialogValue == 0) { - dialogValue = 1; - } - - NumberPickerDialog dialog = new NumberPickerDialog(theme.getThemedDialog(getActivity()), this::setRepeatValue, getResources().getString(R.string.repeat_interval_prompt), - dialogValue, 1, 1, 365); - theme.applyToContext(dialog.getContext()); - dialog.show(); - } - - private void repeatUntilClick() { - Intent intent = new Intent(context, DatePickerActivity.class); - intent.putExtra(DatePickerActivity.EXTRA_TIMESTAMP, repeatUntilValue > 0 ? repeatUntilValue : 0L); - startActivityForResult(intent, REQUEST_PICK_DATE); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_PICK_DATE) { - if (resultCode == Activity.RESULT_OK) { - setRepeatUntilValue(data.getLongExtra(DatePickerActivity.EXTRA_TIMESTAMP, 0L)); - } else { - setRepeatUntilValue(repeatUntilValue); - } - } - super.onActivityResult(requestCode, resultCode, data); - } - private void refreshDisplayView() { if (doRepeat) { displayView.setText(getRepeatString()); displayView.setTextColor(getColor(context, R.color.text_primary)); - clear.setVisibility(View.VISIBLE); } else { - displayView.setText(R.string.repeat_never); + displayView.setText(R.string.repeat_option_does_not_repeat); displayView.setTextColor(getColor(context, R.color.text_tertiary)); - clear.setVisibility(View.GONE); } } private String getRepeatString() { + if (!isCustomValue()) { + switch (frequency) { + case FREQUENCY_DAYS: + return getString(R.string.repeat_option_every_day); + case FREQUENCY_WEEKS: + return getString(R.string.repeat_option_every_week); + case FREQUENCY_MONTHS: + return getString(R.string.repeat_option_every_month); + case FREQUENCY_YEARS: + return getString(R.string.repeat_option_every_year); + } + } + int arrayResource = R.array.repeat_interval; String[] dates = getResources().getStringArray( - arrayResource); - String date = String.format("%s %s", repeatValue, dates[intervalValue]); //$NON-NLS-1$ + arrayResource); + String date = String.format("%s %s", interval, dates[frequency]); //$NON-NLS-1$ if (repeatUntilValue > 0) { return getString(R.string.repeat_detail_duedate_until, date, getDisplayString()); } else { @@ -468,18 +412,11 @@ public class RepeatControlSet extends TaskEditControlFragment { } } - private void updateRepeatUntilOptions() { - repeatUntilOptions.clear(); - if (repeatUntilValue > 0) { - repeatUntilOptions.add(getString(R.string.repeat_until, getDisplayString())); - } - repeatUntilOptions.add(getString(R.string.repeat_forever)); - repeatUntilOptions.add(getString(R.string.repeat_until, "").trim()); - repeatUntilAdapter.notifyDataSetChanged(); - repeatUntil.setSelection(0); + private String getDisplayString() { + return getDisplayString(context, repeatUntilValue); } - private String getDisplayString() { + public static String getDisplayString(Context context, long repeatUntilValue) { StringBuilder displayString = new StringBuilder(); DateTime d = newDateTime(repeatUntilValue); if (d.getMillis() > 0) { diff --git a/app/src/main/java/com/todoroo/astrid/ui/NumberPickerDialog.java b/app/src/main/java/com/todoroo/astrid/ui/NumberPickerDialog.java deleted file mode 100644 index 16f735dab..000000000 --- a/app/src/main/java/com/todoroo/astrid/ui/NumberPickerDialog.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.ui; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.support.v7.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; - -import org.tasks.R; - -public class NumberPickerDialog extends AlertDialog implements OnClickListener { - - public interface OnNumberPickedListener { - void onNumberPicked(int number); - } - - private final NumberPicker mPicker; - private final OnNumberPickedListener mCallback; - - public NumberPickerDialog(Context context, OnNumberPickedListener callBack, - String title, int initialValue, int incrementBy, int start, int end) { - super(context); - mCallback = callBack; - - setButton(DialogInterface.BUTTON_POSITIVE, context.getText(android.R.string.ok), this); - setButton(DialogInterface.BUTTON_NEGATIVE, context.getText(android.R.string.cancel), (OnClickListener) null); - - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.number_picker_dialog, null); - setView(view); - - setTitle(title); - mPicker = view.findViewById(R.id.numberPicker); - mPicker.setIncrementBy(incrementBy); - mPicker.setRange(start, end); - mPicker.setCurrent(initialValue); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if (mCallback != null) { - mPicker.clearFocus(); - mCallback.onNumberPicked(mPicker.getCurrent()); - } - } -} diff --git a/app/src/main/java/org/tasks/locale/Locale.java b/app/src/main/java/org/tasks/locale/Locale.java index d8e1918c3..bde0ca66d 100644 --- a/app/src/main/java/org/tasks/locale/Locale.java +++ b/app/src/main/java/org/tasks/locale/Locale.java @@ -18,6 +18,9 @@ import com.google.common.base.Strings; import org.tasks.R; import java.text.NumberFormat; +import java.text.ParseException; + +import timber.log.Timber; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybeanMR1; @@ -156,6 +159,16 @@ public class Locale { return NumberFormat.getNumberInstance(appLocale).format(number); } + public Integer parseInteger(String number) { + try { + return NumberFormat.getNumberInstance(appLocale) + .parse(number) + .intValue(); + } catch (ParseException e) { + return null; + } + } + public String formatPercentage(int percentage) { return NumberFormat.getPercentInstance(appLocale).format(percentage / 100.0); } diff --git a/app/src/main/java/org/tasks/repeats/CustomRecurrenceDialog.java b/app/src/main/java/org/tasks/repeats/CustomRecurrenceDialog.java new file mode 100644 index 000000000..f31816251 --- /dev/null +++ b/app/src/main/java/org/tasks/repeats/CustomRecurrenceDialog.java @@ -0,0 +1,288 @@ +package org.tasks.repeats; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import com.appeaser.sublimepickerlibrary.drawables.CheckableDrawable; +import com.appeaser.sublimepickerlibrary.recurrencepicker.WeekButton; +import com.todoroo.astrid.repeats.RepeatControlSet; + +import org.tasks.R; +import org.tasks.activities.DatePickerActivity; +import org.tasks.dialogs.DialogBuilder; +import org.tasks.injection.DialogFragmentComponent; +import org.tasks.injection.ForActivity; +import org.tasks.injection.InjectingDialogFragment; +import org.tasks.locale.Locale; +import org.tasks.preferences.ResourceResolver; +import org.tasks.themes.Theme; +import org.tasks.themes.ThemeAccent; + +import java.text.DateFormatSymbols; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnItemSelected; +import butterknife.OnTextChanged; + +import static android.support.v4.content.ContextCompat.getColor; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_DAYS; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_HOURS; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_MINUTES; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_MONTHS; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_WEEKS; +import static com.todoroo.astrid.repeats.RepeatControlSet.FREQUENCY_YEARS; +import static com.todoroo.astrid.repeats.RepeatControlSet.TYPE_COMPLETION_DATE; + +public class CustomRecurrenceDialog extends InjectingDialogFragment { + + public static CustomRecurrenceDialog newCustomRecurrenceDialog(Fragment target) { + CustomRecurrenceDialog dialog = new CustomRecurrenceDialog(); + dialog.setTargetFragment(target, 0); + return dialog; + } + + public interface CustomRecurrenceCallback { + void onSelected(int frequency, int interval, long repeatUntilValue, + boolean repeatAfterCompletion, boolean[] isChecked); + } + + private static final int REQUEST_PICK_DATE = 505; + + @Inject @ForActivity Context context; + @Inject DialogBuilder dialogBuilder; + @Inject Theme theme; + @Inject Locale locale; + + @BindView(R.id.weekGroup) LinearLayout weekGroup1; + @BindView(R.id.weekGroup2) @Nullable LinearLayout weekGroup2; + @BindView(R.id.week_day_1) WeekButton day1; + @BindView(R.id.week_day_2) WeekButton day2; + @BindView(R.id.week_day_3) WeekButton day3; + @BindView(R.id.week_day_4) WeekButton day4; + @BindView(R.id.week_day_5) WeekButton day5; + @BindView(R.id.week_day_6) WeekButton day6; + @BindView(R.id.week_day_7) WeekButton day7; + + @BindView(R.id.repeat_until) Spinner repeatUntilSpinner; + @BindView(R.id.repeatType) Spinner typeSpinner; + @BindView(R.id.frequency) Spinner frequencySpinner; + @BindView(R.id.repeatValue) EditText intervalEditText; + @BindView(R.id.intervalText) TextView intervalTextView; + + private ArrayAdapter repeatUntilAdapter; + private final List repeatUntilOptions = new ArrayList<>(); + private final boolean[] isChecked = new boolean[7]; + + private int frequency; + private int interval; + private long repeatUntilValue; + + private boolean repeatAfterCompletion; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = LayoutInflater.from(getActivity()); + View dialogView = inflater.inflate(R.layout.control_set_repeat, null); + + ButterKnife.bind(this, dialogView); + + ArrayAdapter frequencyAdapter = ArrayAdapter.createFromResource(context, R.array.repeat_frequency, R.layout.frequency_item); + frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + frequencySpinner.setAdapter(frequencyAdapter); + frequencySpinner.setSelection(3); + intervalEditText.setText(locale.formatNumber(1)); + intervalEditText.setSelectAllOnFocus(true); + intervalEditText.selectAll(); + + ArrayAdapter typeAdapter = new ArrayAdapter<>(context, R.layout.simple_spinner_item, getResources().getStringArray(R.array.repeat_type)); + typeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + typeSpinner.setAdapter(typeAdapter); + + repeatUntilAdapter = new ArrayAdapter<>(context, R.layout.simple_spinner_item, repeatUntilOptions); + repeatUntilAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + repeatUntilSpinner.setAdapter(repeatUntilAdapter); + + setInterval(1, true); + + setRepeatUntilValue(repeatUntilValue); + + WeekButton[] weekButtons = new WeekButton[] { day1, day2, day3, day4, day5, day6, day7 }; + int expandedWidthHeight = getResources() + .getDimensionPixelSize(R.dimen.week_button_state_on_circle_size); + + int weekButtonUnselectedTextColor = getColor(context, R.color.text_primary); + int weekButtonSelectedTextColor = ResourceResolver.getData(context, R.attr.fab_text); + WeekButton.setStateColors(weekButtonUnselectedTextColor, weekButtonSelectedTextColor); + + // set up days of week + ThemeAccent accent = theme.getThemeAccent(); + DateFormatSymbols dfs = new DateFormatSymbols(); + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + String[] shortWeekdays = dfs.getShortWeekdays(); + for(int i = 0; i < 7; i++) { + final int index = i; + WeekButton weekButton = weekButtons[i]; + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); + String text = shortWeekdays[dayOfWeek]; + weekButton.setBackgroundDrawable(new CheckableDrawable(accent.getAccentColor(), false, expandedWidthHeight)); + weekButton.setTextColor(weekButtonUnselectedTextColor); + weekButton.setTextOff(text); + weekButton.setTextOn(text); + weekButton.setText(text); + weekButton.setOnCheckedChangeListener((compoundButton, b) -> CustomRecurrenceDialog.this.isChecked[index] = b); + calendar.add(Calendar.DATE, 1); + } + + typeSpinner.setSelection(repeatAfterCompletion ? TYPE_COMPLETION_DATE : RepeatControlSet.TYPE_DUE_DATE); + + return dialogBuilder.newDialog() + .setView(dialogView) + .setPositiveButton(android.R.string.ok, (dialog12, which) -> + ((CustomRecurrenceCallback) getTargetFragment()) + .onSelected(frequency, interval, repeatUntilValue, + repeatAfterCompletion, isChecked)) + .setNegativeButton(android.R.string.cancel, null) + .setOnCancelListener(DialogInterface::dismiss) + .show(); + } + + private void setInterval(int interval, boolean updateEditText) { + this.interval = interval; + if (updateEditText) { + intervalEditText.setText(locale.formatNumber(interval)); + } + updateIntervalTextView(); + } + + private void updateIntervalTextView() { + int resource = getFrequencyPlural(); + String quantityString = getResources().getQuantityString(resource, interval); + intervalTextView.setText(quantityString); + } + + private int getFrequencyPlural() { + switch (frequency) { + case FREQUENCY_MINUTES: + return R.plurals.repeat_minutes; + case FREQUENCY_HOURS: + return R.plurals.repeat_hours; + case FREQUENCY_DAYS: + return R.plurals.repeat_days; + case FREQUENCY_WEEKS: + return R.plurals.repeat_weeks; + case FREQUENCY_MONTHS: + return R.plurals.repeat_months; + case FREQUENCY_YEARS: + return R.plurals.repeat_years; + default: + throw new RuntimeException("Invalid frequency: " + frequency); + } + } + + @OnItemSelected(R.id.repeatType) + public void onRepeatTypeChanged(Spinner spinner, int position) { + repeatAfterCompletion = position == TYPE_COMPLETION_DATE; + } + + @OnItemSelected(R.id.repeat_until) + public void onRepeatUntilChanged(Spinner spinner, int position) { + if (repeatUntilOptions.size() == 2) { + if (position == 0) { + setRepeatUntilValue(0); + } else { + repeatUntilClick(); + } + } else { + if (position == 1) { + setRepeatUntilValue(0); + } else if (position == 2) { + repeatUntilClick(); + } + } + } + + @OnItemSelected(R.id.frequency) + public void onFrequencyChanged(Spinner spinner, int position) { + int weekVisibility = position == RepeatControlSet.FREQUENCY_WEEKS ? View.VISIBLE : View.GONE; + weekGroup1.setVisibility(weekVisibility); + if (weekGroup2 != null) { + weekGroup2.setVisibility(weekVisibility); + } + frequency = position; + updateIntervalTextView(); + } + + @OnTextChanged(R.id.repeatValue) + public void onRepeatValueChanged(CharSequence text) { + Integer value = locale.parseInteger(text.toString()); + if (value == null) { + return; + } + if (value < 1) { + setInterval(1, true); + } else { + setInterval(value, false); + } + } + + private void setRepeatUntilValue(long newValue) { + repeatUntilValue = newValue; + updateRepeatUntilOptions(); + } + + private void repeatUntilClick() { + Intent intent = new Intent(context, DatePickerActivity.class); + intent.putExtra(DatePickerActivity.EXTRA_TIMESTAMP, repeatUntilValue > 0 ? repeatUntilValue : 0L); + startActivityForResult(intent, REQUEST_PICK_DATE); + } + + private void updateRepeatUntilOptions() { + repeatUntilOptions.clear(); + if (repeatUntilValue > 0) { + repeatUntilOptions.add(getString(R.string.repeat_until, RepeatControlSet.getDisplayString(context, repeatUntilValue))); + } + repeatUntilOptions.add(getString(R.string.repeat_forever)); + repeatUntilOptions.add(getString(R.string.repeat_until, "").trim()); + repeatUntilAdapter.notifyDataSetChanged(); + repeatUntilSpinner.setSelection(0); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_PICK_DATE) { + if (resultCode == Activity.RESULT_OK) { + setRepeatUntilValue(data.getLongExtra(DatePickerActivity.EXTRA_TIMESTAMP, 0L)); + } else { + setRepeatUntilValue(repeatUntilValue); + } + } + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void inject(DialogFragmentComponent component) { + component.inject(this); + } +} diff --git a/app/src/main/java/org/tasks/themes/Theme.java b/app/src/main/java/org/tasks/themes/Theme.java index a6fe83260..637cddb79 100644 --- a/app/src/main/java/org/tasks/themes/Theme.java +++ b/app/src/main/java/org/tasks/themes/Theme.java @@ -40,6 +40,10 @@ public class Theme { return themeColor; } + public ThemeAccent getThemeAccent() { + return themeAccent; + } + public LayoutInflater getLayoutInflater(Context context) { return (LayoutInflater) wrap(context).getSystemService(Context.LAYOUT_INFLATER_SERVICE); } diff --git a/app/src/main/res/layout-w820dp/week_buttons.xml b/app/src/main/res/layout-w820dp/week_buttons.xml new file mode 100644 index 000000000..64599df98 --- /dev/null +++ b/app/src/main/res/layout-w820dp/week_buttons.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/control_set_repeat.xml b/app/src/main/res/layout/control_set_repeat.xml index 28cb6fa35..4773078be 100644 --- a/app/src/main/res/layout/control_set_repeat.xml +++ b/app/src/main/res/layout/control_set_repeat.xml @@ -3,120 +3,111 @@ ** ** See the file "LICENSE" for the full license governing this code. --> - + android:orientation="vertical"> + android:orientation="vertical"> - + android:background="?colorAccentDialog"> -