diff --git a/app/src/main/java/com/philliphsu/clock2/BaseCursorAdapter.java b/app/src/main/java/com/philliphsu/clock2/BaseCursorAdapter.java index bb7575e..72bfc0a 100644 --- a/app/src/main/java/com/philliphsu/clock2/BaseCursorAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/BaseCursorAdapter.java @@ -21,7 +21,7 @@ public abstract class BaseCursorAdapter< private final OnListItemInteractionListener mListener; private C mCursor; - protected abstract VH onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener); + protected abstract VH onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener, int viewType); public BaseCursorAdapter(OnListItemInteractionListener listener) { mListener = listener; @@ -40,7 +40,7 @@ public abstract class BaseCursorAdapter< */ @Override public VH onCreateViewHolder(ViewGroup parent, int viewType) { - return onCreateViewHolder(parent, mListener); + return onCreateViewHolder(parent, mListener, viewType); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java b/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java index 740f11a..88861ce 100644 --- a/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/BaseViewHolder.java @@ -48,7 +48,7 @@ public abstract class BaseViewHolder extends RecyclerView.ViewHolder implemen @Override public final void onClick(View v) { if (mListener != null) { - mListener.onListItemClick(mItem); + mListener.onListItemClick(mItem, getAdapterPosition()); } } } diff --git a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java index 664820b..098f27f 100644 --- a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java +++ b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java @@ -12,6 +12,6 @@ package com.philliphsu.clock2; * be easily adapted to not just Fragments, but also custom Views, Activities, etc. */ public interface OnListItemInteractionListener { - void onListItemClick(T item); + void onListItemClick(T item, int position); void onListItemDeleted(T item); } diff --git a/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java index 7f684e1..b27e234 100644 --- a/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java @@ -36,9 +36,13 @@ public abstract class RecyclerViewFragment< public abstract void onFabClick(); /** - * @return the adapter to set on the RecyclerView + * @return the adapter to set on the RecyclerView. SUBCLASSES MUST OVERRIDE THIS, BECAUSE THE + * DEFAULT IMPLEMENTATION WILL ALWAYS RETURN AN UNINITIALIZED ADAPTER INSTANCE. */ - protected abstract A getAdapter(); + @Nullable + protected A getAdapter() { + return mAdapter; + } /** * @return the LayoutManager to set on the RecyclerView. The default implementation diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java index 9bf5112..e438ce0 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java @@ -9,7 +9,8 @@ import com.philliphsu.clock2.OnListItemInteractionListener; import java.util.Arrays; import java.util.List; -public class AlarmsAdapter extends BaseAdapter { +@Deprecated +public class AlarmsAdapter extends BaseAdapter { public AlarmsAdapter(List alarms, OnListItemInteractionListener listener) { super(Alarm.class, alarms, listener); @@ -22,8 +23,8 @@ public class AlarmsAdapter extends BaseAdapter { } @Override - public AlarmViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { - return new AlarmViewHolder(parent, listener); + public BaseAlarmViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { + return new CollapsedAlarmViewHolder(parent, listener); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java index 064058f..8fdcbde 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java @@ -1,22 +1,28 @@ package com.philliphsu.clock2.alarms; +import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.BaseCursorAdapter; import com.philliphsu.clock2.OnListItemInteractionListener; +import com.philliphsu.clock2.model.AlarmCursor; import com.philliphsu.clock2.util.AlarmController; /** * Created by Phillip Hsu on 6/29/2016. - * - * TODO: Extend from BaseCursorAdapter */ -public class AlarmsCursorAdapter extends BaseCursorAdapter { +public class AlarmsCursorAdapter extends BaseCursorAdapter { private static final String TAG = "AlarmsCursorAdapter"; + private static final int VIEW_TYPE_COLLAPSED = 0; + private static final int VIEW_TYPE_EXPANDED = 1; private final AlarmController mAlarmController; + // TOneverDO: initial value >= 0 + private int mExpandedPosition = RecyclerView.NO_POSITION; + private long mExpandedId = RecyclerView.NO_ID; + public AlarmsCursorAdapter(OnListItemInteractionListener listener, AlarmController alarmController) { super(listener); @@ -24,7 +30,56 @@ public class AlarmsCursorAdapter extends BaseCursorAdapter listener) { - return new AlarmViewHolder(parent, listener, mAlarmController); + protected BaseAlarmViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener, int viewType) { + if (viewType == VIEW_TYPE_COLLAPSED) + return new CollapsedAlarmViewHolder(parent, listener, mAlarmController); + return new ExpandedAlarmViewHolder(parent, listener, mAlarmController); + } + + @Override + public int getItemViewType(int position) { + final long stableId = getItemId(position); + return stableId != RecyclerView.NO_ID && stableId == mExpandedId + ? VIEW_TYPE_EXPANDED : VIEW_TYPE_COLLAPSED; + } + +// // TODO +// public void saveInstance(Bundle outState) { +// outState.putLong(KEY_EXPANDED_ID, mExpandedId); +// } + + public boolean expand(int position) { + final long stableId = getItemId(position); + if (mExpandedId == stableId) { + return false; + } + mExpandedId = stableId; + // If we can call this, the item is in view, so we don't need to scroll to it? +// mScrollHandler.smoothScrollTo(position); + if (mExpandedPosition >= 0) { + // Collapse this position first. getItemViewType() will be called + // in onCreateViewHolder() to verify which ViewHolder to create + // for the position. + notifyItemChanged(mExpandedPosition); + } + mExpandedPosition = position; + notifyItemChanged(position); + return true; + + // This would be my alternative solution. But we're keeping Google's + // because the stable ID *could* hold up better for orientation changes + // than the position? I.e. when saving instance state we save the id. +// int oldExpandedPosition = mExpandedPosition; +// mExpandedPosition = position; +// if (oldExpandedPosition >= 0) { +// notifyItemChanged(oldExpandedPosition); +// } +// notifyItemChanged(mExpandedPosition); + } + + public void collapse(int position) { + mExpandedId = RecyclerView.NO_ID; + mExpandedPosition = RecyclerView.NO_POSITION; + notifyItemChanged(position); } } 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 959edbb..c089676 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; +import android.support.annotation.Nullable; import android.support.v4.content.Loader; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -20,11 +21,10 @@ import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.DelayedSnackbarHandler; import butterknife.Bind; -import butterknife.ButterKnife; public class AlarmsFragment extends RecyclerViewFragment< Alarm, - AlarmViewHolder, + BaseAlarmViewHolder, AlarmCursor, AlarmsCursorAdapter> implements ScrollHandler { private static final String TAG = "AlarmsFragment"; @@ -32,7 +32,7 @@ public class AlarmsFragment extends RecyclerViewFragment< // Public because MainActivity needs to use it. public static final int REQUEST_CREATE_ALARM = 1; - private AlarmsCursorAdapter mAdapter; +// private AlarmsCursorAdapter mAdapter; private AsyncItemChangeHandler mAsyncItemChangeHandler; private AlarmController mAlarmController; private Handler mHandler = new Handler(); @@ -81,12 +81,6 @@ public class AlarmsFragment extends RecyclerViewFragment< DelayedSnackbarHandler.makeAndShow(mSnackbarAnchor); } - @Override - public void onDestroyView() { - super.onDestroyView(); - ButterKnife.unbind(this); // Only for fragments! - } - @Override public Loader onCreateLoader(int id, Bundle args) { return new AlarmsListCursorLoader(getActivity()); @@ -95,7 +89,8 @@ public class AlarmsFragment extends RecyclerViewFragment< @Override public void onLoadFinished(Loader loader, AlarmCursor data) { super.onLoadFinished(loader, data); - // Scroll to the last modified alarm + // This may have been a requery due to content change. If the change + // was an insertion, scroll to the last modified alarm. performScrollToStableId(); } @@ -105,12 +100,13 @@ public class AlarmsFragment extends RecyclerViewFragment< startActivityForResult(intent, REQUEST_CREATE_ALARM); } + @Nullable @Override protected AlarmsCursorAdapter getAdapter() { - if (mAdapter == null) { - mAdapter = new AlarmsCursorAdapter(this, mAlarmController); - } - return mAdapter; + if (super.getAdapter() != null) + return super.getAdapter(); + // Create a new adapter + return new AlarmsCursorAdapter(this, mAlarmController); } @Override @@ -154,10 +150,14 @@ public class AlarmsFragment extends RecyclerViewFragment< } @Override - public void onListItemClick(Alarm item) { - Intent intent = new Intent(getActivity(), EditAlarmActivity.class); - intent.putExtra(EditAlarmActivity.EXTRA_ALARM_ID, item.id()); - startActivityForResult(intent, REQUEST_EDIT_ALARM); + public void onListItemClick(Alarm item, int position) { +// Intent intent = new Intent(getActivity(), EditAlarmActivity.class); +// intent.putExtra(EditAlarmActivity.EXTRA_ALARM_ID, item.id()); +// startActivityForResult(intent, REQUEST_EDIT_ALARM); + boolean expanded = getAdapter().expand(position); + if (!expanded) { + getAdapter().collapse(position); + } } @Override @@ -173,21 +173,22 @@ public class AlarmsFragment extends RecyclerViewFragment< private void performScrollToStableId() { if (mScrollToStableId != RecyclerView.NO_ID) { int position = -1; - for (int i = 0; i < mAdapter.getItemCount(); i++) { - if (mAdapter.getItemId(i) == mScrollToStableId) { + for (int i = 0; i < getAdapter().getItemCount(); i++) { + if (getAdapter().getItemId(i) == mScrollToStableId) { position = i; break; } } if (position >= 0) { scrollToPosition(position); + // We were called because of a requery due to an insertion. + getAdapter().expand(position); } } // Reset mScrollToStableId = RecyclerView.NO_ID; } - @Deprecated @Override public void onListItemDeleted(final Alarm item) { diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java similarity index 53% rename from app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java rename to app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java index 0276360..f8776dc 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java @@ -1,5 +1,6 @@ package com.philliphsu.clock2.alarms; +import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.v7.widget.SwitchCompat; import android.text.format.DateFormat; @@ -11,11 +12,9 @@ import android.widget.TextView; import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.BaseViewHolder; -import com.philliphsu.clock2.DaysOfWeek; import com.philliphsu.clock2.OnListItemInteractionListener; import com.philliphsu.clock2.R; import com.philliphsu.clock2.editalarm.TimeTextUtils; -import com.philliphsu.clock2.model.AlarmsRepository; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.AlarmUtils; @@ -28,34 +27,25 @@ import butterknife.OnTouch; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS; import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; /** - * Created by Phillip Hsu on 5/31/2016. + * Created by Phillip Hsu on 7/31/2016. */ -public class AlarmViewHolder extends BaseViewHolder implements AlarmCountdown.OnTickListener { +public abstract class BaseAlarmViewHolder extends BaseViewHolder { + private final AlarmController mAlarmController; @Bind(R.id.time) TextView mTime; @Bind(R.id.on_off_switch) SwitchCompat mSwitch; @Bind(R.id.label) TextView mLabel; - @Bind(R.id.countdown) AlarmCountdown mCountdown; - @Bind(R.id.recurring_days) TextView mDays; @Bind(R.id.dismiss) Button mDismissButton; - @Deprecated // TODO: Delete this, the only usage is from AlarmsAdapter (SortedList), which is not used anymore. - public AlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { - super(parent, R.layout.item_alarm, listener); - mAlarmController = null; - mCountdown.setOnTickListener(this); - } - - public AlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener, - AlarmController alarmController) { - super(parent, R.layout.item_alarm, listener); - mAlarmController = alarmController; - mCountdown.setOnTickListener(this); + public BaseAlarmViewHolder(ViewGroup parent, @LayoutRes int layoutRes, + OnListItemInteractionListener listener, + AlarmController controller) { + super(parent, layoutRes, listener); + mAlarmController = controller; } @Override @@ -63,15 +53,29 @@ public class AlarmViewHolder extends BaseViewHolder implements AlarmCount super.onBind(alarm); bindTime(new Date(alarm.ringsAt())); bindSwitch(alarm.isEnabled()); - bindCountdown(alarm.isEnabled(), alarm.ringsIn()); bindDismissButton(alarm); - bindLabel(alarm); - bindDays(alarm); + bindLabel(alarm.label()); } - @Override - public void onTick() { - mCountdown.showAsText(getAlarm().ringsIn()); + /** + * Exposed to subclasses if they have different visibility criteria. + * The default criteria for visibility is if {@code label} has + * a non-zero length. + */ + protected void bindLabel(boolean visible, String label) { + setVisibility(mLabel, visible); + mLabel.setText(label); + } + + /** + * Exposed to subclasses if they have visibility logic for their views. + */ + protected final void setVisibility(@NonNull View view, boolean visible) { + view.setVisibility(visible ? VISIBLE : GONE); + } + + protected final Alarm getAlarm() { + return getItem(); } @OnClick(R.id.dismiss) @@ -99,26 +103,6 @@ public class AlarmViewHolder extends BaseViewHolder implements AlarmCount */ } - // Changed in favor or OnCheckedChange - /* - @Deprecated - @OnClick(R.id.on_off_switch) - void toggle() { - Alarm alarm = getAlarm(); - alarm.setEnabled(mSwitch.isChecked()); - if (alarm.isEnabled()) { - AlarmUtils.scheduleAlarm(getContext(), alarm); - bindCountdown(true, alarm.ringsIn()); - bindDismissButton(alarm); - } else { - AlarmUtils.cancelAlarm(getContext(), alarm); // might save repo - bindCountdown(false, -1); - bindDismissButton(false, ""); - } - save(); - } - */ - @OnTouch(R.id.on_off_switch) boolean slide(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { @@ -127,9 +111,28 @@ public class AlarmViewHolder extends BaseViewHolder implements AlarmCount return false; // proceed as usual } +// // Changed in favor of OnCheckedChanged +// @Deprecated +// @OnClick(R.id.on_off_switch) +// void toggle() { +// Alarm alarm = getAlarm(); +// alarm.setEnabled(mSwitch.isChecked()); +// if (alarm.isEnabled()) { +// AlarmUtils.scheduleAlarm(getContext(), alarm); +// bindCountdown(true, alarm.ringsIn()); +// bindDismissButton(alarm); +// } else { +// AlarmUtils.cancelAlarm(getContext(), alarm); // might save repo +// bindCountdown(false, -1); +// bindDismissButton(false, ""); +// } +// save(); +// } + @OnCheckedChanged(R.id.on_off_switch) void toggle(boolean checked) { - if (mSwitch.isPressed()) { // needed to distinguish automatic calls when VH binds from actual presses + // http://stackoverflow.com/q/27641705/5055032 + if (mSwitch.isPressed()) { // filters out automatic calls from VH binding // don't need to toggle the switch state Alarm alarm = getAlarm(); alarm.setEnabled(checked); @@ -158,80 +161,18 @@ public class AlarmViewHolder extends BaseViewHolder implements AlarmCount mSwitch.setChecked(enabled); } - private void bindCountdown(boolean enabled, long remainingTime) { - if (enabled) { - mCountdown.showAsText(remainingTime); - mCountdown.startTicking(true); - mCountdown.setVisibility(VISIBLE); - } else { - mCountdown.stopTicking(); - mCountdown.setVisibility(GONE); - } - } - private void bindDismissButton(Alarm alarm) { int hoursBeforeUpcoming = AlarmUtils.hoursBeforeUpcoming(getContext()); boolean visible = alarm.isEnabled() && (alarm.ringsWithinHours(hoursBeforeUpcoming) || alarm.isSnoozed()); String buttonText = alarm.isSnoozed() ? getContext().getString(R.string.title_snoozing_until, formatTime(getContext(), alarm.snoozingUntil())) : getContext().getString(R.string.dismiss_now); - bindDismissButton(visible, buttonText); - } - - private void bindDismissButton(boolean visible, String buttonText) { setVisibility(mDismissButton, visible); mDismissButton.setText(buttonText); } - private void bindLabel(Alarm alarm) { - // Should also be visible even if alarm has no label so mCountdown is properly positioned next - // to mLabel. That is, mCountdown's layout position is dependent on mLabel being present. - boolean labelVisible = alarm.label().length() > 0 || mCountdown.getVisibility() == VISIBLE; - bindLabel(labelVisible, alarm.label()); - } - - private void bindLabel(boolean visible, String label) { - setVisibility(mLabel, visible); - mLabel.setText(label); - } - - private void bindDays(Alarm alarm) { - int num = alarm.numRecurringDays(); - String text; - if (num == NUM_DAYS) { - text = getContext().getString(R.string.every_day); - } else if (num == 0) { - text = ""; - } else { - StringBuilder sb = new StringBuilder(); - for (int i = 0 /* Ordinal days*/; i < NUM_DAYS; i++) { - // What day is at this position in the week? - int weekDay = DaysOfWeek.getInstance(getContext()).weekDayAt(i); - if (alarm.isRecurring(weekDay)) { - sb.append(DaysOfWeek.getLabel(weekDay)).append(", "); - } - } - // Cut off the last comma and space - sb.delete(sb.length() - 2, sb.length()); - text = sb.toString(); - } - bindDays(num > 0, text); - } - - private void bindDays(boolean visible, String text) { - setVisibility(mDays, visible); - mDays.setText(text); - } - - private void setVisibility(@NonNull View view, boolean visible) { - view.setVisibility(visible ? VISIBLE : GONE); - } - - private Alarm getAlarm() { - return getItem(); - } - - private void save() { - AlarmsRepository.getInstance(getContext()).saveItems(); + private void bindLabel(String label) { + boolean visible = label.length() > 0; + bindLabel(visible, label); } } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java new file mode 100644 index 0000000..db7285f --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java @@ -0,0 +1,95 @@ +package com.philliphsu.clock2.alarms; + +import android.view.ViewGroup; +import android.widget.TextView; + +import com.philliphsu.clock2.Alarm; +import com.philliphsu.clock2.DaysOfWeek; +import com.philliphsu.clock2.OnListItemInteractionListener; +import com.philliphsu.clock2.R; +import com.philliphsu.clock2.util.AlarmController; + +import butterknife.Bind; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS; + +/** + * Created by Phillip Hsu on 5/31/2016. + */ +public class CollapsedAlarmViewHolder extends BaseAlarmViewHolder implements AlarmCountdown.OnTickListener { + + @Bind(R.id.countdown) AlarmCountdown mCountdown; // TODO: Make your own chronometer with minute precision. + @Bind(R.id.recurring_days) TextView mDays; // TODO: use `new DateFormatSymbols().getShortWeekdays()` to set texts + + @Deprecated // TODO: Delete this, the only usage is from AlarmsAdapter (SortedList), which is not used anymore. + public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { + super(parent, R.layout.item_alarm, listener, null); + mCountdown.setOnTickListener(this); + } + + public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener, + AlarmController alarmController) { + super(parent, R.layout.item_alarm, listener, alarmController); + mCountdown.setOnTickListener(this); + } + + @Override + public void onBind(Alarm alarm) { + super.onBind(alarm); + bindCountdown(alarm.isEnabled(), alarm.ringsIn()); + bindDays(alarm); + } + + @Override + public void onTick() { + mCountdown.showAsText(getAlarm().ringsIn()); + } + + private void bindCountdown(boolean enabled, long remainingTime) { + if (enabled) { + mCountdown.showAsText(remainingTime); + mCountdown.startTicking(true); + mCountdown.setVisibility(VISIBLE); + } else { + mCountdown.stopTicking(); + mCountdown.setVisibility(GONE); + } + } + + @Override + protected void bindLabel(boolean visible, String label) { + // Should also be visible even if label has zero length so mCountdown is properly positioned + // next to mLabel. That is, mCountdown's layout position is dependent on mLabel being present. + super.bindLabel(visible || mCountdown.getVisibility() == VISIBLE, label); + } + + private void bindDays(Alarm alarm) { + int num = alarm.numRecurringDays(); + String text; + if (num == NUM_DAYS) { + text = getContext().getString(R.string.every_day); + } else if (num == 0) { + text = ""; + } else { + StringBuilder sb = new StringBuilder(); + for (int i = 0 /* Ordinal days*/; i < NUM_DAYS; i++) { + // What day is at this position in the week? + int weekDay = DaysOfWeek.getInstance(getContext()).weekDayAt(i); + if (alarm.isRecurring(weekDay)) { + sb.append(DaysOfWeek.getLabel(weekDay)).append(", "); + } + } + // Cut off the last comma and space + sb.delete(sb.length() - 2, sb.length()); + text = sb.toString(); + } + bindDays(num > 0, text); + } + + private void bindDays(boolean visible, String text) { + setVisibility(mDays, visible); + mDays.setText(text); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java new file mode 100644 index 0000000..b99d2a7 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/alarms/ExpandedAlarmViewHolder.java @@ -0,0 +1,112 @@ +package com.philliphsu.clock2.alarms; + +import android.media.RingtoneManager; +import android.net.Uri; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ToggleButton; + +import com.philliphsu.clock2.Alarm; +import com.philliphsu.clock2.DaysOfWeek; +import com.philliphsu.clock2.OnListItemInteractionListener; +import com.philliphsu.clock2.R; +import com.philliphsu.clock2.util.AlarmController; + +import butterknife.Bind; +import butterknife.OnCheckedChanged; +import butterknife.OnClick; + +/** + * Created by Phillip Hsu on 7/31/2016. + */ +public class ExpandedAlarmViewHolder extends BaseAlarmViewHolder { + + @Bind(R.id.save) Button mSave; + @Bind(R.id.delete) Button mDelete; + @Bind(R.id.ringtone) Button mRingtone; + @Bind(R.id.vibrate) CheckBox mVibrate; + @Bind({R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6}) + ToggleButton[] mDays; + + public ExpandedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener, + AlarmController controller) { + super(parent, R.layout.item_expanded_alarm, listener, controller); + } + + @Override + public void onBind(Alarm alarm) { + super.onBind(alarm); + bindDays(alarm); + bindRingtone(alarm.ringtone()); + bindVibrate(alarm.vibrates()); + } + + @Override + protected void bindLabel(boolean visible, String label) { + super.bindLabel(true, label); + } + + @OnClick(R.id.save) + void save() { + // TODO + } + + @OnClick(R.id.delete) + void delete() { + // TODO + } + + @OnClick(R.id.ringtone) + void showRingtonePickerDialog() { + // TODO + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // 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 + // we should commit changes as they are made. + @OnCheckedChanged(R.id.vibrate) + void onVibrateToggled() { + // TODO + } + + @OnCheckedChanged({ R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6 }) + void onDayToggled() { + // TODO + } + /////////////////////////////////////////////////////////////////////////////////////////// + + private void bindDays(Alarm alarm) { + for (int i = 0; i < mDays.length; i++) { + int weekDay = DaysOfWeek.getInstance(getContext()).weekDayAt(i); + String label = DaysOfWeek.getLabel(weekDay); + mDays[i].setTextOn(label); + mDays[i].setTextOff(label); + mDays[i].setChecked(alarm.isRecurring(weekDay)); + } + } + + private void bindRingtone(String ringtone) { + // Initializing to Settings.System.DEFAULT_ALARM_ALERT_URI will show + // "Default ringtone (Name)" on the button text, and won't show the + // selection on the dialog when first opened. (unless you choose to show + // the default item in the intent extra?) + // Compare with getDefaultUri(int), which returns the symbolic URI instead of the + // actual sound URI. For TYPE_ALARM, this actually returns the same constant. + Uri mSelectedRingtoneUri; // TODO: This was actually an instance variable in EditAlarmActivity. + if (null == ringtone || ringtone.isEmpty()) { + mSelectedRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri( + getContext(), RingtoneManager.TYPE_ALARM); + } else { + mSelectedRingtoneUri = Uri.parse(ringtone); + } + String title = RingtoneManager.getRingtone(getContext(), + mSelectedRingtoneUri).getTitle(getContext()); + mRingtone.setText(title); + } + + private void bindVibrate(boolean vibrates) { + mVibrate.setChecked(vibrates); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java b/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java index 4778c45..398d3f6 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java @@ -17,7 +17,7 @@ public class TimersCursorAdapter extends BaseCursorAdapter listener) { + protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener, int viewType) { return new TimerViewHolder(parent, listener); } } diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java index 21d20de..9d4342c 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java @@ -3,6 +3,7 @@ package com.philliphsu.clock2.timers; import android.content.Intent; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.v4.content.Loader; import com.philliphsu.clock2.RecyclerViewFragment; @@ -30,8 +31,12 @@ public class TimersFragment extends RecyclerViewFragment< startActivityForResult(intent, REQUEST_CREATE_TIMER); } + @Nullable @Override protected TimersCursorAdapter getAdapter() { + if (super.getAdapter() != null) + return super.getAdapter(); + // Create a new adapter return new TimersCursorAdapter(this); } @@ -41,7 +46,7 @@ public class TimersFragment extends RecyclerViewFragment< } @Override - public void onListItemClick(Timer item) { + public void onListItemClick(Timer item, int position) { } diff --git a/app/src/main/res/layout/alarm_time_layout.xml b/app/src/main/res/layout/alarm_time_layout.xml new file mode 100644 index 0000000..9dca22a --- /dev/null +++ b/app/src/main/res/layout/alarm_time_layout.xml @@ -0,0 +1,28 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_alarm.xml b/app/src/main/res/layout/item_alarm.xml index 502cde3..b0688f5 100644 --- a/app/src/main/res/layout/item_alarm.xml +++ b/app/src/main/res/layout/item_alarm.xml @@ -9,6 +9,9 @@ android:orientation="vertical" android:background="?selectableItemBackground"> + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + +