diff --git a/app/src/main/java/com/philliphsu/clock2/AddLabelDialog.java b/app/src/main/java/com/philliphsu/clock2/AddLabelDialog.java index 44b34a7..b76a354 100644 --- a/app/src/main/java/com/philliphsu/clock2/AddLabelDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/AddLabelDialog.java @@ -89,4 +89,8 @@ public class AddLabelDialog extends AppCompatDialogFragment { }); return alert; } + + public void setOnLabelSetListener(OnLabelSetListener l) { + mOnLabelSetListener = l; + } } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java index 0ee5366..3e8a5ce 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -6,10 +6,8 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v4.content.Loader; -import android.text.format.DateFormat; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -19,8 +17,7 @@ import com.philliphsu.clock2.AsyncAlarmsTableUpdateHandler; import com.philliphsu.clock2.R; import com.philliphsu.clock2.RecyclerViewFragment; import com.philliphsu.clock2.editalarm.BaseTimePickerDialog; -import com.philliphsu.clock2.editalarm.NumberGridTimePickerDialog; -import com.philliphsu.clock2.editalarm.NumpadTimePickerDialog; +import com.philliphsu.clock2.editalarm.TimePickerHelper; import com.philliphsu.clock2.model.AlarmCursor; import com.philliphsu.clock2.model.AlarmsListCursorLoader; import com.philliphsu.clock2.util.AlarmController; @@ -34,7 +31,8 @@ public class AlarmsFragment extends RecyclerViewFragment< implements ScrollHandler, // TODO: Move interface to base class BaseTimePickerDialog.OnTimeSetListener { private static final String TAG = "AlarmsFragment"; - private static final String TAG_TIME_PICKER = "time_picker"; + + static final String TAG_TIME_PICKER = "time_picker"; // TODO: Delete these constants. We no longer use EditAlarmActivity. // @Deprecated @@ -118,23 +116,7 @@ public class AlarmsFragment extends RecyclerViewFragment< // If we keep a reference to the dialog, we keep its previous state as well. // So the next time we call show() on it, the input field will show the // last inputted time. - BaseTimePickerDialog dialog = null; - String numpadStyle = getString(R.string.number_pad); - String gridStyle = getString(R.string.grid_selector); - String prefTimePickerStyle = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString( - // key for the preference value to retrieve - getString(R.string.key_time_picker_style), - // default value - numpadStyle); - if (prefTimePickerStyle.equals(numpadStyle)) { - dialog = NumpadTimePickerDialog.newInstance(this); - } else if (prefTimePickerStyle.equals(gridStyle)) { - dialog = NumberGridTimePickerDialog.newInstance( - this, // OnTimeSetListener - 0, // Initial hour of day - 0, // Initial minute - DateFormat.is24HourFormat(getActivity())); - } + BaseTimePickerDialog dialog = TimePickerHelper.newDialog(getActivity(), this); // DISREGARD THE LINT WARNING ABOUT DIALOG BEING NULL. dialog.show(getFragmentManager(), TAG_TIME_PICKER); } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java index a7df937..31601dd 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java @@ -4,7 +4,9 @@ import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; +import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SwitchCompat; import android.text.format.DateFormat; import android.util.Log; @@ -14,10 +16,14 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; +import com.philliphsu.clock2.AddLabelDialog; import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.BaseViewHolder; import com.philliphsu.clock2.OnListItemInteractionListener; import com.philliphsu.clock2.R; +import com.philliphsu.clock2.editalarm.BaseTimePickerDialog; +import com.philliphsu.clock2.editalarm.BaseTimePickerDialog.OnTimeSetListener; +import com.philliphsu.clock2.editalarm.TimePickerHelper; import com.philliphsu.clock2.editalarm.TimeTextUtils; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.AlarmUtils; @@ -38,11 +44,20 @@ import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; */ public abstract class BaseAlarmViewHolder extends BaseViewHolder { private static final String TAG = "BaseAlarmViewHolder"; + private static final String TAG_ADD_LABEL_DIALOG = "add_label_dialog"; private final AlarmController mAlarmController; // TODO: Should we use VectorDrawable type? private final Drawable mDismissNowDrawable; private final Drawable mCancelSnoozeDrawable; + private final FragmentManager mFragmentManager; + + // These should only be changed from the OnTimeSet callback. + // If we had initialized these in onBind(), they would be reset to their original values + // from the Alarm each time the ViewHolder binds. + // A value of -1 indicates that the Alarm's time has not been changed. + int mSelectedHourOfDay = -1; + int mSelectedMinute = -1; @Bind(R.id.time) TextView mTime; @Bind(R.id.on_off_switch) SwitchCompat mSwitch; @@ -58,6 +73,27 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder { // Instead, we create and cache the Drawables once. mDismissNowDrawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_dismiss_alarm_24dp); mCancelSnoozeDrawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_cancel_snooze); + + // TODO: This is bad! Use a Controller/Presenter instead... + // or simply pass in an instance of FragmentManager to the ctor. + AppCompatActivity act = (AppCompatActivity) getContext(); + mFragmentManager = act.getSupportFragmentManager(); + + // Are we recreating this because of a rotation? + // If so, try finding any dialog that was last shown in our backstack, + // and restore the callback. + BaseTimePickerDialog picker = (BaseTimePickerDialog) + mFragmentManager.findFragmentByTag(AlarmsFragment.TAG_TIME_PICKER); + if (picker != null) { + Log.i(TAG, "Restoring time picker callback"); + picker.setOnTimeSetListener(newOnTimeSetListener()); + } + AddLabelDialog labelDialog = (AddLabelDialog) + mFragmentManager.findFragmentByTag(TAG_ADD_LABEL_DIALOG); + if (labelDialog != null) { + Log.i(TAG, "Restoring add label callback"); + labelDialog.setOnLabelSetListener(newOnLabelSetListener()); + } } @Override @@ -149,7 +185,7 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder { Alarm alarm = getAlarm(); alarm.setEnabled(checked); if (alarm.isEnabled()) { - // TODO: On Moto X, upcoming notification doesn't post immediately + // TODO: On 21+, upcoming notification doesn't post immediately mAlarmController.scheduleAlarm(alarm, true); mAlarmController.save(alarm); } else { @@ -162,7 +198,14 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder { @OnClick(R.id.time) void openTimePicker() { - Log.d(TAG, "Time clicked!"); + BaseTimePickerDialog dialog = TimePickerHelper.newDialog(getContext(), newOnTimeSetListener()); + dialog.show(mFragmentManager, AlarmsFragment.TAG_TIME_PICKER); + } + + @OnClick(R.id.label) + void openLabelEditor() { + AddLabelDialog dialog = AddLabelDialog.newInstance(newOnLabelSetListener(), mLabel.getText()); + dialog.show(mFragmentManager, TAG_ADD_LABEL_DIALOG); } private void bindTime(Alarm alarm) { @@ -217,4 +260,37 @@ public abstract class BaseAlarmViewHolder extends BaseViewHolder { boolean visible = label.length() > 0; bindLabel(visible, label); } + + private AddLabelDialog.OnLabelSetListener newOnLabelSetListener() { + // Create a new listener per request. This is primarily used for + // setting the dialog callback again after a rotation. + // + // If we saved a reference to a listener, it would be tied to + // its ViewHolder instance. ViewHolders are reused, so we + // could accidentally leak this reference to other Alarm items + // in the list. + return new AddLabelDialog.OnLabelSetListener() { + @Override + public void onLabelSet(String label) { + mLabel.setText(label); + } + }; + } + + private OnTimeSetListener newOnTimeSetListener() { + // Create a new listener per request. This is primarily used for + // setting the dialog callback again after a rotation. + // + // If we saved a reference to a listener, it would be tied to + // its ViewHolder instance. ViewHolders are reused, so we + // could accidentally leak this reference to other Alarm items + // in the list. + return new OnTimeSetListener() { + @Override + public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) { + mSelectedHourOfDay = hourOfDay; + mSelectedMinute = minute; + } + }; + } } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java index 5c644e1..ba865d9 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java @@ -93,4 +93,9 @@ public class CollapsedAlarmViewHolder extends BaseAlarmViewHolder { setVisibility(mDays, visible); mDays.setText(text); } + + @Override + void openLabelEditor() { + // DO NOT IMPLEMENT + } } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java index 6cafe8c..a661376 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java @@ -7,7 +7,6 @@ import android.graphics.drawable.Drawable; import android.media.RingtoneManager; import android.net.Uri; import android.support.v4.graphics.drawable.DrawableCompat; -import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -15,7 +14,6 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.ToggleButton; -import com.philliphsu.clock2.AddLabelDialog; import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.DaysOfWeek; import com.philliphsu.clock2.OnListItemInteractionListener; @@ -27,6 +25,9 @@ import butterknife.Bind; import butterknife.OnCheckedChanged; import butterknife.OnClick; +import static com.philliphsu.clock2.DaysOfWeek.SATURDAY; +import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; + /** * Created by Phillip Hsu on 7/31/2016. */ @@ -64,8 +65,14 @@ public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder { .ringtone(""/*TODO*/) .vibrates(mVibrate.isChecked()) .build(); - Log.d(TAG, "New alarm: " + newAlarm); oldAlarm.copyMutableFieldsTo(newAlarm); + // ---------------------------------------------- + // TOneverDO: precede copyMutableFieldsTo() + newAlarm.setEnabled(mSwitch.isChecked()); + for (int i = SUNDAY; i <= SATURDAY; i++) { + newAlarm.setRecurring(i, isRecurringDay(i)); + } + // ---------------------------------------------- listener.onListItemUpdate(newAlarm, getAdapterPosition()); } }); @@ -130,20 +137,6 @@ public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder { ((Activity) getContext()).startActivityForResult(intent, AlarmsFragment.REQUEST_PICK_RINGTONE); } - @OnClick(R.id.label) // The label view is in our superclass, but we can reference it. - void openLabelEditor() { - AddLabelDialog dialog = AddLabelDialog.newInstance(new AddLabelDialog.OnLabelSetListener() { - @Override - public void onLabelSet(String label) { - mLabel.setText(label); - // TODO: persist change. Use TimerController and its update() - } - }, mLabel.getText()); - // TODO: This is bad! Use a Controller instead! - AppCompatActivity act = (AppCompatActivity) getContext(); - dialog.show(act.getSupportFragmentManager(), "TAG"); - } - /////////////////////////////////////////////////////////////////////////////////////////// // We didn't have to write code like this in EditAlarmActivity, because we never committed // any changes until the user explicitly clicked save. We have to do this here now because @@ -204,4 +197,11 @@ public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder { RingtoneManager.getActualDefaultRingtoneUri(getContext(), RingtoneManager.TYPE_ALARM) : Uri.parse(ringtone); } + + private boolean isRecurringDay(int weekDay) { + // What position in the week is this day located at? + int pos = DaysOfWeek.getInstance(getContext()).positionOf(weekDay); + // Return the state of this day according to its button + return mDays[pos].isChecked(); + } } diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/TimePickerHelper.java b/app/src/main/java/com/philliphsu/clock2/editalarm/TimePickerHelper.java new file mode 100644 index 0000000..10a4ced --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/TimePickerHelper.java @@ -0,0 +1,41 @@ +package com.philliphsu.clock2.editalarm; + +import android.content.Context; +import android.preference.PreferenceManager; +import android.text.format.DateFormat; + +import com.philliphsu.clock2.R; + +/** + * Created by Phillip Hsu on 9/2/2016. + * + * Helper for creating a time picker dialog. + */ +public final class TimePickerHelper { + + public static BaseTimePickerDialog newDialog(Context context, BaseTimePickerDialog.OnTimeSetListener l) { + BaseTimePickerDialog dialog = null; + String numpadStyle = context.getString(R.string.number_pad); + String gridStyle = context.getString(R.string.grid_selector); + String prefTimePickerStyle = PreferenceManager.getDefaultSharedPreferences(context).getString( + // key for the preference value to retrieve + context.getString(R.string.key_time_picker_style), + // default value + numpadStyle); + if (prefTimePickerStyle.equals(numpadStyle)) { + dialog = NumpadTimePickerDialog.newInstance(l); + } else if (prefTimePickerStyle.equals(gridStyle)) { + dialog = NumberGridTimePickerDialog.newInstance( + l, // OnTimeSetListener + 0, // Initial hour of day + 0, // Initial minute + DateFormat.is24HourFormat(context)); + } + // We don't have a default case, because we don't need one; prefTimePickerStyle + // will ALWAYS match one of numpadStyle or gridStyle. As such, the dialog returned + // from here will NEVER be null. + return dialog; + } + + private TimePickerHelper() {} +}