diff --git a/app/src/main/java/com/philliphsu/clock2/Alarm.java b/app/src/main/java/com/philliphsu/clock2/Alarm.java index add1938..d87a381 100644 --- a/app/src/main/java/com/philliphsu/clock2/Alarm.java +++ b/app/src/main/java/com/philliphsu/clock2/Alarm.java @@ -2,10 +2,8 @@ package com.philliphsu.clock2; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; import com.google.auto.value.AutoValue; -import com.philliphsu.clock2.model.JsonSerializable; import com.philliphsu.clock2.model.ObjectWithId; import org.json.JSONObject; @@ -22,7 +20,7 @@ import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; * Created by Phillip Hsu on 5/26/2016. */ @AutoValue -public abstract class Alarm extends ObjectWithId implements JsonSerializable, Parcelable { +public abstract class Alarm extends ObjectWithId implements Parcelable { private static final int MAX_MINUTES_CAN_SNOOZE = 30; // =================== MUTABLE ======================= @@ -211,26 +209,6 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours); } - // TODO: Rename to getIntId() so usages refer to ObjectWithId#getIntId(), then delete this method. - public int intId() { - return getIntId(); - } - - // TODO: Remove method signature from JsonSerializable interface. - // TODO: Remove final modifier. - // TODO: Rename to getId() so usages refer to ObjectWithId#getId(), then delete this method. - @Override - public final long id() { - return getId(); - } - - @Deprecated - @Override - @NonNull - public JSONObject toJsonObject() { - throw new UnsupportedOperationException(); - } - // ============================ PARCELABLE ============================== // Unfortunately, we can't use the Parcelable extension for AutoValue because // our model isn't totally immutable. Our mutable properties will be left diff --git a/app/src/main/java/com/philliphsu/clock2/BaseAdapter.java b/app/src/main/java/com/philliphsu/clock2/BaseAdapter.java deleted file mode 100644 index 689db1a..0000000 --- a/app/src/main/java/com/philliphsu/clock2/BaseAdapter.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.philliphsu.clock2; - -import android.support.v7.util.SortedList; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.util.SortedListAdapterCallback; -import android.view.ViewGroup; - -import java.util.List; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -public abstract class BaseAdapter> extends RecyclerView.Adapter { - - private final OnListItemInteractionListener mListener; - private final SortedList mItems; - - protected BaseAdapter(Class cls, List items, OnListItemInteractionListener listener) { - mItems = new SortedList<>(cls, new SortedListAdapterCallback(this) { - @Override - public int compare(T o1, T o2) { - //return BaseAdapter.this.*compare(o1, o2); // *: See note below - return BaseAdapter.this.compare(o1, o2); - // Only need to specify the type when calling a _generic_ method - // that defines _its own type parameters_ in its signature, but even - // then, it's not actually necessary because the compiler can - // infer the type. - // This is just an _abstract_ method that takes params of type T. - } - - @Override - public boolean areContentsTheSame(T oldItem, T newItem) { - return BaseAdapter.this.areContentsTheSame(oldItem, newItem); - } - - @Override - public boolean areItemsTheSame(T item1, T item2) { - return BaseAdapter.this.areItemsTheSame(item1, item2); - } - }); - mListener = listener; - mItems.addAll(items); - } - - protected abstract VH onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener); - - protected abstract int compare(T o1, T o2); - - protected abstract boolean areContentsTheSame(T oldItem, T newItem); - - protected abstract boolean areItemsTheSame(T item1, T item2); - - @Override // not final to allow subclasses to use the viewType if needed - public VH onCreateViewHolder(ViewGroup parent, int viewType) { - return onCreateViewHolder(parent, mListener); - } - - @Override - public final void onBindViewHolder(VH holder, int position) { - holder.onBind(mItems.get(position)); - } - - @Override - public final int getItemCount() { - return mItems.size(); - } - - public final void replaceData(List items) { - mItems.clear(); - mItems.addAll(items); - } - - protected final T getItem(int position) { - return mItems.get(position); - } - - public final int addItem(T item) { - return mItems.add(item); - } - - public final boolean removeItem(T item) { - return mItems.remove(item); - } - - public final void updateItem(T oldItem, T newItem) { - // SortedList finds the index of an item by using its callback's compare() method. - // We can describe our current item update process is as follows: - // * An item's fields are modified - // * The changes are saved to the repository - // * A item update callback is fired to the RV - // * The RV calls its adapter's updateItem(), passing the instance of the modified item as both arguments - // (because modifying an item keeps the same instance of the item) - // * The SortedList tries to find the index of the param oldItem, but since its fields are changed, - // the search may end up failing because compare() could return the wrong index. - // A workaround is to copy construct the original item instance BEFORE you modify the fields. - // Then, oldItem should point to the copied instance. - // Alternatively, a better approach is to make items immutable. - mItems.updateItemAt(mItems.indexOf(oldItem), newItem); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/DurationDisplayer.java b/app/src/main/java/com/philliphsu/clock2/DurationDisplayer.java deleted file mode 100644 index 61473ce..0000000 --- a/app/src/main/java/com/philliphsu/clock2/DurationDisplayer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.philliphsu.clock2; - -import android.support.annotation.NonNull; - -/** - * Created by Phillip Hsu on 4/30/2016. - */ -public interface DurationDisplayer { - /** - * Callback interface to be implemented by a parent/host of this displayer. - * The host should use this to tell its displayer to update its duration text - * via showAsText(long). - */ - /*public*/ interface OnTickListener { - // Listeners implement this to be notified of when - // they should update this displayer's text - void onTick(); - } - - /** Hosts of this displayer use this to attach themselves */ - void setOnTickListener(@NonNull OnTickListener listener); - void startTicking(boolean resume); - void stopTicking(); - void forceTick(); - void showAsText(long duration); -} diff --git a/app/src/main/java/com/philliphsu/clock2/Numpad.java b/app/src/main/java/com/philliphsu/clock2/Numpad.java deleted file mode 100644 index 8cd1a23..0000000 --- a/app/src/main/java/com/philliphsu/clock2/Numpad.java +++ /dev/null @@ -1,266 +0,0 @@ -package com.philliphsu.clock2; - -import android.content.Context; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.util.AttributeSet; -import android.view.View; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.Space; -import android.widget.TableLayout; -import android.widget.TableRow; - -import java.util.Arrays; - -import butterknife.Bind; -import butterknife.ButterKnife; -import butterknife.OnClick; - -/** - * Created by Phillip Hsu on 6/2/2016. - */ -public abstract class Numpad extends TableLayout { - private static final String TAG = "Numpad"; - private static final int NUM_COLUMNS = 3; - private static final int RADIX_10 = 10; - protected static final int UNMODIFIED = -1; - - // Derived classes need to build this themselves via buildBackspace(). - private ImageButton mBackspace; - private ImageButton mCollapse; - private int[] mInput; - private int mCount = 0; - private KeyListener mKeyListener; - - @Bind({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four, - R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine }) - Button[] mButtons; - - public interface KeyListener { - void onNumberInput(String number); - void onCollapse(); - void onBackspace(String newStr); - void onLongBackspace(); - } - - public Numpad(Context context) { - super(context); - init(); - } - - public Numpad(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public void setKeyListener(@NonNull KeyListener listener) { - mKeyListener = listener; - } - - protected KeyListener getKeyListener() { - return mKeyListener; - } - - protected abstract int capacity(); - - protected final void enable(int lowerLimitInclusive, int upperLimitExclusive) { - if (lowerLimitInclusive < 0 || upperLimitExclusive > mButtons.length) - throw new IndexOutOfBoundsException("Upper limit out of range"); - - for (int i = 0; i < mButtons.length; i++) - mButtons[i].setEnabled(i >= lowerLimitInclusive && i < upperLimitExclusive); - } - - protected final int valueAt(int index) { - checkIndexWithinBounds(index); - return mInput[index]; - } - - protected final int count() { - return mCount; - } - - protected final int getInput() { - String currentInput = ""; - for (int i : mInput) - if (i != UNMODIFIED) - currentInput += i; - return Integer.parseInt(currentInput); - } - - protected final void buildBackspace(int r, int c) { - mBackspace = (ImageButton) buildButton(R.layout.numpad_backspace, r, c); - - mBackspace.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - backspace(); - } - }); - - mBackspace.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - return longBackspace(); - } - }); - } - - protected final void buildCollapse(int r, int c) { - mCollapse = (ImageButton) buildButton(R.layout.numpad_collapse, r, c); - - mCollapse.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - checkKeyListenerSet(); - mKeyListener.onCollapse(); - } - }); - } - - protected final void newRow() { - TableRow newRow = new TableRow(getContext()); - newRow.setLayoutParams(new TableRow.LayoutParams()); - addView(newRow); - for (int i = 0; i < NUM_COLUMNS; i++) { - Space s = new Space(getContext()); - setButtonLayoutParams(s); - newRow.addView(s); - } - } - - protected final View buildButton(@LayoutRes int buttonRes, int r, int c) { - View button = View.inflate(getContext(), buttonRes, null); - setButtonLayoutParams(button); - replace(r, c, button); - return button; - } - - @OnClick({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four, - R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine }) - protected void onClick(Button button) { - onClick(button, mCount); - } - - public void onClick(Button button, int at) { - if (mCount < mInput.length) { - checkIndexWithinBounds(at); - mInput[at] = Integer.parseInt(button.getText().toString()); - mCount++; - } - } - - /** Performs an artificial click on the Button with the specified digit. */ - protected final void performClick(int digit) { - if (digit < 0 || digit >= mButtons.length) - throw new ArrayIndexOutOfBoundsException("No Button with digit " + digit); - if (!mButtons[digit].isEnabled()) - throw new IllegalArgumentException("Button " + digit + " is disabled. " + - "Did you call AlarmNumpad.setInput(String) with an invalid time?"); - onClick(mButtons[digit]); - } - - /** Performs an artificial click on the Button with the specified digit. */ - protected final void performClick(char charDigit) { - performClick(asDigit(charDigit)); - } - - protected void setInput(int... digits) { - if (digits.length != mInput.length) - throw new IllegalArgumentException("Input arrays not the same length"); - for (int i = 0; i < digits.length; i++) { - if (digits[i] < 0 || digits[i] > 9) - throw new IllegalArgumentException("Element in input out of range"); - if (!mButtons[i].isEnabled()) - throw new IllegalStateException("Button with digit " + digits[i] + " is disabled"); - mInput[i] = digits[i]; - } - } - - protected void backspace() { - if (mCount - 1 >= 0) { - mInput[--mCount] = UNMODIFIED; - } - } - - // public to allow hosts of this numpad to modify its contents - public void backspace(int at) { - if (at < 0 || at > mInput.length /* at == mInput.length is valid */) - throw new IndexOutOfBoundsException("Cannot backspace on index " + at); - if (at - 1 >= 0) { - mInput[--at] = UNMODIFIED; - mCount--; - } - } - - protected boolean longBackspace() { - Arrays.fill(mInput, UNMODIFIED); - mCount = 0; - return true; - } - - protected void setBackspaceEnabled(boolean enabled) { - mBackspace.setEnabled(enabled); - } - - protected final void notifyOnNumberInputListener(String number) { - checkKeyListenerSet(); - mKeyListener.onNumberInput(number); - } - - protected final void notifyOnBackspaceListener(String newStr) { - checkKeyListenerSet(); - mKeyListener.onBackspace(newStr); - } - - protected final void notifyOnLongBackspaceListener() { - checkKeyListenerSet(); - mKeyListener.onLongBackspace(); - } - - private void setButtonLayoutParams(View target) { - target.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.MATCH_PARENT, 1)); - } - - private void replace(int r, int c, View newView) { - checkLocation(r, c); - TableRow row = (TableRow) getChildAt(r); - row.removeViewAt(c); - row.addView(newView, c); - } - - // Checks if the specified location in the View hierarchy exists. - private void checkLocation(int r, int c) { - if (r < 0 || r >= getChildCount()) - throw new IndexOutOfBoundsException("No TableRow at row " + r); - if (c < 0 || c >= NUM_COLUMNS) - throw new IndexOutOfBoundsException("No column " + c + " at row " + r); - } - - private void checkIndexWithinBounds(int i) { - if (i < 0 || i >= mInput.length) { - throw new ArrayIndexOutOfBoundsException("Index " + i + "out of bounds"); - } - } - - private void checkKeyListenerSet() { - if (null == mKeyListener) - throw new NullPointerException("Numpad Key listener not set"); - } - - private int asDigit(char charDigit) { - if (!Character.isDigit(charDigit)) - throw new IllegalArgumentException("Character is not a digit"); - return Character.digit(charDigit, RADIX_10); - } - - private void init() { - View.inflate(getContext(), R.layout.content_numpad, this); - ButterKnife.bind(this); - if (capacity() < 0) - throw new IllegalArgumentException("Negative capacity"); - mInput = new int[capacity()]; - Arrays.fill(mInput, UNMODIFIED); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java index 144a10f..28f0311 100644 --- a/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java +++ b/app/src/main/java/com/philliphsu/clock2/OnListItemInteractionListener.java @@ -2,14 +2,6 @@ package com.philliphsu.clock2; /** * Created by Phillip Hsu on 5/31/2016. - * This interface MUST be extended by Fragments that display a RecyclerView as a list. - * The reason for this is Fragments need to do an instanceof check on their host Context - * to see if it implements this interface, and instanceof cannot be used with generic type - * parameters. Why not just define this interface as a member of the Fragment class? - * Because the Fragment's BaseAdapter needs a reference to this interface, and we don't want - * to couple the BaseAdapter - * to the Fragment. By keeping this interface as generic as possible, the BaseAdapter can - * be easily adapted to not just Fragments, but also custom Views, Activities, etc. */ public interface OnListItemInteractionListener { void onListItemClick(T item, int position); diff --git a/app/src/main/java/com/philliphsu/clock2/SharedPreferencesHelper.java b/app/src/main/java/com/philliphsu/clock2/SharedPreferencesHelper.java deleted file mode 100644 index 839fe1e..0000000 --- a/app/src/main/java/com/philliphsu/clock2/SharedPreferencesHelper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.philliphsu.clock2; - -import android.support.annotation.StringRes; - -/** - * Created by Phillip Hsu on 6/6/2016. - */ -@Deprecated -public interface SharedPreferencesHelper { - /** Suitable for retrieving the value of a ListPreference */ - int getInt(@StringRes int key, int defaultValue); -} diff --git a/app/src/main/java/com/philliphsu/clock2/TickHandler.java b/app/src/main/java/com/philliphsu/clock2/TickHandler.java deleted file mode 100644 index 5e16ae2..0000000 --- a/app/src/main/java/com/philliphsu/clock2/TickHandler.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.philliphsu.clock2; - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.support.annotation.NonNull; - -import com.philliphsu.clock2.DurationDisplayer.OnTickListener; - -import static com.philliphsu.clock2.util.Preconditions.checkNotNull; - -/** - * Created by Phillip Hsu on 4/18/2016. - */ -public class TickHandler extends Handler { - private static final int MSG_TICK = 0; - private static final int MSG_FORCE_TICK = 1; - - @NonNull private final OnTickListener mOnTickListener; - private final long mTickInterval; - - public TickHandler(@NonNull OnTickListener listener, long tickInterval) { - super(Looper.getMainLooper()); - mOnTickListener = checkNotNull(listener); - mTickInterval = tickInterval; - } - - @Override - public void handleMessage(Message msg) { - // Account for the time showDuration() takes to execute - // and subtract that off from the countdown interval - // (so the countdown doesn't get postponed by however long - // showDuration() takes) - long startOnTick = SystemClock.elapsedRealtime(); - mOnTickListener.onTick(); - long countdownRemainder = mTickInterval - - (SystemClock.elapsedRealtime() - startOnTick); - - // special case: onTick took longer than countdown - // interval to complete, skip to next interval - while (countdownRemainder < 0) - countdownRemainder += mTickInterval; - - if (msg.what == MSG_TICK) { // as opposed to MSG_FORCE_TICK - sendMessageDelayed(obtainMessage(MSG_TICK), mTickInterval); - } - } - - public void startTicking(boolean resume) { - if (hasMessages(MSG_TICK)) - return; - if (resume) { - sendMessage(obtainMessage(MSG_TICK)); - } else { - sendMessageDelayed(obtainMessage(MSG_TICK), mTickInterval); - } - } - - public void stopTicking() { - removeMessages(MSG_TICK); - } - - /** - * Forces a single call to {@link OnTickListener#onTick() onTick()} - * without scheduling looped messages on this handler. - */ - public void forceTick() { - sendMessage(obtainMessage(MSG_FORCE_TICK)); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java index 2aa9fd6..45e565f 100644 --- a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java +++ b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java @@ -13,7 +13,7 @@ import com.philliphsu.clock2.util.AlarmController; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.app.PendingIntent.FLAG_ONE_SHOT; -import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; +import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime; // TODO: Consider registering this locally instead of in the manifest. public class UpcomingAlarmReceiver extends BroadcastReceiver { diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmActivity.java index de8b7a4..312d300 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmActivity.java @@ -12,7 +12,7 @@ import com.philliphsu.clock2.R; import com.philliphsu.clock2.ringtone.RingtoneActivity; import com.philliphsu.clock2.ringtone.RingtoneService; import com.philliphsu.clock2.util.AlarmController; -import com.philliphsu.clock2.util.DateFormatUtils; +import com.philliphsu.clock2.util.TimeFormatUtils; public class AlarmActivity extends RingtoneActivity { private static final String TAG = "AlarmActivity"; @@ -117,7 +117,7 @@ public class AlarmActivity extends RingtoneActivity { } private void postMissedAlarmNote() { - String alarmTime = DateFormatUtils.formatTime(this, + String alarmTime = TimeFormatUtils.formatTime(this, getRingingObject().hour(), getRingingObject().minutes()); Notification note = new NotificationCompat.Builder(this) .setContentTitle(getString(R.string.missed_alarm)) diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmCountdown.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmCountdown.java index 6b583f9..184c005 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmCountdown.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmCountdown.java @@ -35,8 +35,9 @@ import java.util.Locale; * Created by Phillip Hsu on 4/30/2016. * * A modified version of the framework's Chronometer class that counts down toward the base time, - * uses the {@link System#currentTimeMillis()} timebase, and only has minute precision. It displays - * the current timer value in the form "In H hrs and M mins". // TODO: This isn't the actual format. Verify what that is. + * uses the {@link System#currentTimeMillis()} timebase, and only has minute precision. + * + * TODO: Refactor by using the ChronometerDelegate class. */ public class AlarmCountdown extends TextView { private static final String TAG = "AlarmCountdown"; diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmRingtoneService.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmRingtoneService.java index 99367b4..4c6ccd1 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmRingtoneService.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmRingtoneService.java @@ -12,7 +12,7 @@ import com.philliphsu.clock2.ringtone.RingtoneService; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.AlarmUtils; -import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; +import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime; public class AlarmRingtoneService extends RingtoneService { private static final String TAG = "AlarmRingtoneService"; diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java deleted file mode 100644 index e438ce0..0000000 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.philliphsu.clock2.alarms; - -import android.view.ViewGroup; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.BaseAdapter; -import com.philliphsu.clock2.OnListItemInteractionListener; - -import java.util.Arrays; -import java.util.List; - -@Deprecated -public class AlarmsAdapter extends BaseAdapter { - - public AlarmsAdapter(List alarms, OnListItemInteractionListener listener) { - super(Alarm.class, alarms, listener); - setHasStableIds(true); - } - - @Override - public long getItemId(int position) { - return getItem(position).id(); - } - - @Override - public BaseAlarmViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { - return new CollapsedAlarmViewHolder(parent, listener); - } - - @Override - public int compare(Alarm o1, Alarm o2) { - return Long.compare(o1.ringsAt(), o2.ringsAt()); - } - - @Override - public boolean areContentsTheSame(Alarm oldItem, Alarm newItem) { - return oldItem.hour() == newItem.hour() - && oldItem.minutes() == newItem.minutes() - && oldItem.isEnabled() == newItem.isEnabled() - && oldItem.label().equals(newItem.label()) - && oldItem.ringsIn() == newItem.ringsIn() - && Arrays.equals(oldItem.recurringDays(), newItem.recurringDays()) - && oldItem.snoozingUntil() == newItem.snoozingUntil(); - } - - @Override - public boolean areItemsTheSame(Alarm item1, Alarm item2) { - return item1.id() == item2.id(); - } -} 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 26b0565..9acc4b5 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/BaseAlarmViewHolder.java @@ -25,7 +25,7 @@ import com.philliphsu.clock2.R; import com.philliphsu.clock2.TimePickerDialogController; import com.philliphsu.clock2.aospdatetimepicker.Utils; import com.philliphsu.clock2.editalarm.BaseTimePickerDialog.OnTimeSetListener; -import com.philliphsu.clock2.editalarm.TimeTextUtils; +import com.philliphsu.clock2.util.TimeTextUtils; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.AlarmUtils; import com.philliphsu.clock2.util.FragmentTagUtils; @@ -39,7 +39,7 @@ import butterknife.OnTouch; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; +import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime; /** * Created by Phillip Hsu on 7/31/2016. 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 137890c..d0c0365 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/CollapsedAlarmViewHolder.java @@ -23,11 +23,6 @@ public class CollapsedAlarmViewHolder extends BaseAlarmViewHolder { @Bind(R.id.countdown) AlarmCountdown mCountdown; @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_collapsed_alarm, listener, null); - } - public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener listener, AlarmController alarmController) { super(parent, R.layout.item_collapsed_alarm, listener, alarmController); diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java b/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java deleted file mode 100644 index 4f25afe..0000000 --- a/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.philliphsu.clock2.alarms.dummy; - -import com.philliphsu.clock2.Alarm; - -import java.util.ArrayList; -import java.util.List; - -/** - * Helper class for providing sample content for user interfaces created by - * Android template wizards. - *

- * TODO: Replace all uses of this class before publishing your app. - */ -public class DummyContent { - - /** - * An array of sample (dummy) items. - */ - public static final List ITEMS = new ArrayList<>(); - - private static final int COUNT = 10; - - static { - // Add some sample items. - for (int i = 1; i <= COUNT; i++) { - addItem(createAlarm(i)); - } - } - - private static void addItem(Alarm item) { - ITEMS.add(item); - } - - private static Alarm createAlarm(int position) { - return null; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmContract.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmContract.java deleted file mode 100644 index 76e3ff1..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmContract.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import java.util.Date; - -/** - * Created by Phillip Hsu on 6/2/2016. - */ -@Deprecated -interface AlarmContract { - - interface View { - void showLabel(String label); - void showEnabled(boolean enabled); - void showCanDismissNow(); - void showSnoozed(Date snoozingUntilMillis); - } - - interface Presenter { - void dismissNow(); - void stopSnoozing(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java deleted file mode 100644 index b682d27..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.KeyEvent; - -/** - * Couldn't figure out how to draw the numpad over the system keyboard, so - * the callback interface provided here is of no use to us. - */ -@Deprecated -public class AlarmEditText extends android.support.v7.widget.AppCompatEditText { - - private OnBackPressListener mOnBackPressListener; - - public AlarmEditText(Context context) { - super(context); - } - - public AlarmEditText(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public AlarmEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK - && event.getAction() == KeyEvent.ACTION_UP - && mOnBackPressListener != null) { - mOnBackPressListener.onBackPress(); - } - return super.dispatchKeyEvent(event); // See http://stackoverflow.com/a/5993196/5055032 - } - - public void setOnBackPressListener(OnBackPressListener onBackPressListener) { - mOnBackPressListener = onBackPressListener; - } - - public interface OnBackPressListener { - void onBackPress(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmItemContract.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmItemContract.java deleted file mode 100644 index 62358f4..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmItemContract.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -/** - * Created by Phillip Hsu on 6/2/2016. - * - * TODO: Consider NOT extending from AlarmContract. Instead, define the methods - * specific to this interface. Make a base implementation class of the base - * AlarmContract. When you create an implementation class of the more specific - * interface, all you need to do is implement the more specific interface. - * The base impl class will already implement the base interface methods. - * That way, each concrete interface impl doesn't need to implement the - * base interface methods again and again. - */ -public interface AlarmItemContract { - - interface View extends AlarmContract.View { - void showTime(String time); - void showCountdown(long remainingTime); - void showRecurringDays(String recurringDays); - } - - interface Presenter extends AlarmContract.Presenter { - void setEnabled(boolean enabled); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java deleted file mode 100644 index fe4a4fb..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java +++ /dev/null @@ -1,463 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.content.Context; -import android.support.annotation.IntDef; -import android.support.design.widget.FloatingActionButton; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.view.View; -import android.widget.Button; -import android.widget.FrameLayout; - -import com.philliphsu.clock2.Numpad; -import com.philliphsu.clock2.R; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Created by Phillip Hsu on 6/2/2016. - */ -public class AlarmNumpad extends Numpad { - private static final String TAG = "AlarmNumpad"; - - // Time can be represented with maximum of 4 digits - private static final int MAX_DIGITS = 4; - - // Formatted time string has a maximum of 8 characters - // in the 12-hour clock, e.g 12:59 AM. Although the 24-hour - // clock should be capped at 5 characters, the difference - // is not significant enough to deal with the separate cases. - private static final int MAX_CHARS = 8; - - private static final int UNSPECIFIED = -1; - private static final int AM = 0; - private static final int PM = 1; - private static final int HRS_24 = 2; - - @IntDef({ UNSPECIFIED, AM, PM, HRS_24 }) // Specifies the accepted constants - @Retention(RetentionPolicy.SOURCE) // Usages do not need to be recorded in .class files - private @interface AmPmState {} - - private Button leftAlt; // AM or :00 - private Button rightAlt; // PM or :30 - private FloatingActionButton fab; - private final StringBuilder mFormattedInput = new StringBuilder(MAX_CHARS); - - @AmPmState - private int mAmPmState = UNSPECIFIED; - - public AlarmNumpad(Context context) { - this(context, null); - } - - public AlarmNumpad(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** Returns the hour of day (0-23) regardless of clock system */ - public int getHours() { - if (!checkTimeValid()) - throw new IllegalStateException("Cannot call getHours() until legal time inputted"); - int hours = count() < 4 ? valueAt(0) : valueAt(0) * 10 + valueAt(1); - if (hours == 12) { - switch (mAmPmState) { - case AM: - return 0; - case PM: - case HRS_24: - return 12; - default: - break; - } - } - - // AM/PM clock needs value offset - return hours + (mAmPmState == PM ? 12 : 0); - } - - public int getMinutes() { - if (!checkTimeValid()) - throw new IllegalStateException("Cannot call getMinutes() until legal time inputted"); - return count() < 4 ? valueAt(1) * 10 + valueAt(2) : valueAt(2) * 10 + valueAt(3); - } - - /** - * Checks if the input stored so far qualifies as a valid time. - * For this to return {@code true}, the hours, minutes AND AM/PM - * state must be set. - */ - public boolean checkTimeValid() { - if (mAmPmState == UNSPECIFIED || mAmPmState == HRS_24 && count() < 3) - return false; - // AM or PM can only be set if the time was already valid previously, so we don't need - // to check for them. - return true; - } - - @Override - protected int capacity() { - return MAX_DIGITS; - } - - @Override - protected void onClick(Button button) { - super.onClick(button); - // Format and store the input as text - inputNumber(button.getText()); - notifyOnNumberInputListener(mFormattedInput.toString()); - updateNumpadStates(); - } - - @Override - protected void backspace() { - int len = mFormattedInput.length(); - if (!is24HourFormat() && mAmPmState != UNSPECIFIED) { - mAmPmState = UNSPECIFIED; - // Delete starting from index of space to end - mFormattedInput.delete(mFormattedInput.indexOf(" "), len); - } else { - super.backspace(); - mFormattedInput.delete(len - 1, len); - if (count() == 3) { - // Move the colon from its 4-digit position to its 3-digit position, - // unless doing so gives an invalid time. - // e.g. 17:55 becomes 1:75, which is invalid. - // All 3-digit times in the 12-hour clock at this point should be - // valid. The limits <=155 and (>=200 && <=235) are really only - // imposed on the 24-hour clock, and were chosen because 4-digit times - // in the 24-hour clock can only go up to 15:5[0-9] or be within the range - // [20:00, 23:59] if they are to remain valid when they become three digits. - // The is24HourFormat() check is therefore unnecessary. - int value = getInput(); - if (value <= 155 || value >= 200 && value <= 235) { - mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":")); - mFormattedInput.insert(1, ":"); - } - } else if (count() == 2) { - // Remove the colon - mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":")); - } - } - - notifyOnBackspaceListener(mFormattedInput.toString()); - updateNumpadStates(); - } - - @Override - protected boolean longBackspace() { - boolean consumed = super.longBackspace(); - mFormattedInput.delete(0, mFormattedInput.length()); - updateNumpadStates(); - mAmPmState = UNSPECIFIED; - notifyOnLongBackspaceListener(); - return consumed; - } - - public void setTime(int hours, int minutes) { - if (hours < 0 || hours > 23) - throw new IllegalArgumentException("Illegal hours: " + hours); - if (minutes < 0 || minutes > 59) - throw new IllegalArgumentException("Illegal minutes: " + minutes); - - // Internal representation of the time has been checked for legality. - // Now we need to format it depending on the user's clock system. - // If 12-hour clock, can't set mAmPmState yet or else this interferes - // with the button state update mechanism. Instead, cache the state - // the hour would resolve to in a local variable and set it after - // all digits are inputted. - int amPmState; - if (!is24HourFormat()) { - // Convert 24-hour times into 12-hour compatible times. - if (hours == 0) { - hours = 12; - amPmState = AM; - } else if (hours == 12) { - amPmState = PM; - } else if (hours > 12) { - hours -= 12; - amPmState = PM; - } else { - amPmState = AM; - } - } else { - amPmState = HRS_24; - } - - // Only if on 24-hour clock, zero-pad single digit hours. - // Zero cannot be the first digit of any time in the 12-hour clock. - String strHrs = is24HourFormat() - ? String.format("%02d", hours) - : String.valueOf(hours); - String strMins = String.format("%02d", minutes); // Zero-pad single digit minutes - - // TODO: Do this off the main thread - for (int i = 0; i < strHrs.length(); i++) - performClick(strHrs.charAt(i)); - for (int i = 0; i < strMins.length(); i++) - performClick(strMins.charAt(i)); - - mAmPmState = amPmState; - if (mAmPmState != HRS_24) { - onAltButtonClick(mAmPmState == AM ? leftAlt : rightAlt); - } - } - - public String getTime() { - return mFormattedInput.toString(); - } - - private void updateAltButtonStates() { - if (count() == 0) { - // No input, no access! - leftAlt.setEnabled(false); - rightAlt.setEnabled(false); - } else if (count() == 1) { - // Any of 0-9 inputted, always have access in either clock. - leftAlt.setEnabled(true); - rightAlt.setEnabled(true); - } else if (count() == 2) { - // Any 2 digits that make a valid hour for either clock are eligible for access - int time = getInput(); - boolean validTwoDigitHour = is24HourFormat() ? time <= 23 : time >= 10 && time <= 12; - leftAlt.setEnabled(validTwoDigitHour); - rightAlt.setEnabled(validTwoDigitHour); - } else if (count() == 3) { - if (is24HourFormat()) { - // For the 24-hour clock, no access at all because - // two more digits (00 or 30) cannot be added to 3 digits. - leftAlt.setEnabled(false); - rightAlt.setEnabled(false); - } else { - // True for any 3 digits, if AM/PM not already entered - boolean enabled = mAmPmState == UNSPECIFIED; - leftAlt.setEnabled(enabled); - rightAlt.setEnabled(enabled); - } - } else if (count() == MAX_DIGITS) { - // If all 4 digits are filled in, the 24-hour clock has absolutely - // no need for the alt buttons. However, The 12-hour clock has - // complete need of them, if not already used. - boolean enabled = !is24HourFormat() && mAmPmState == UNSPECIFIED; - leftAlt.setEnabled(enabled); - rightAlt.setEnabled(enabled); - } - } - - private void updateBackspaceState() { - setBackspaceEnabled(count() > 0); - } - - private void updateFabState() { - // Special case of 0 digits is legal (that is the default hint time) - if (count() == 0) { - fab.setEnabled(true); - return; - } - // Minimum of 3 digits is required - if (count() < 3) { - fab.setEnabled(false); - return; - } - - if (is24HourFormat()) { - int time = getInput(); - fab.setEnabled(time % 100 <= 59); - } else { - // If on 12-hour clock, FAB will never be enabled - // until AM or PM is explicitly clicked. - fab.setEnabled(mAmPmState != UNSPECIFIED); - } - } - - private void updateNumpadStates() { - updateAltButtonStates(); - updateFabState(); - updateBackspaceState(); - updateNumberKeysStates(); - } - - private void updateNumberKeysStates() { - int cap = 10; // number of buttons - boolean is24hours = is24HourFormat(); - - if (count() == 0) { - enable(is24hours ? 0 : 1, cap); - return; - } else if (count() == MAX_DIGITS) { - enable(0, 0); - return; - } - - int time = getInput(); - if (is24hours) { - if (count() == 1) { - enable(0, time < 2 ? cap : 6); - } else if (count() == 2) { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 6); - } else if (count() == 3) { - if (time >= 236) { - enable(0, 0); - } else { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } else { - if (count() == 1) { - if (time == 0) { - throw new IllegalStateException("12-hr format, zeroth digit = 0?"); - } else { - enable(0, 6); - } - } else if (count() == 2 || count() == 3) { - if (time >= 126) { - enable(0, 0); - } else { - if (time >= 100 && time <= 125 && mAmPmState != UNSPECIFIED) { - // Could legally input fourth digit, if not for the am/pm state already set - enable(0, 0); - } else { - enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } - } - } - - // Helper method for inputting each character of an altBtn. - private void onAltButtonClick(Button altBtn) { - if (leftAlt != altBtn && rightAlt != altBtn) - throw new IllegalArgumentException("Not called with one of the alt buttons"); - - // Manually insert special characters for 12-hour clock - if (!is24HourFormat()) { - if (count() <= 2) { - // The colon is inserted for you - performClick(0); - performClick(0); - } - // text is AM or PM, so include space before - mFormattedInput.append(' ').append(altBtn.getText()); - mAmPmState = leftAlt == altBtn ? AM : PM; - // Digits will be shown for you on click, but not AM/PM - notifyOnNumberInputListener(mFormattedInput.toString()); - } else { - // Need to consider each character individually, - // as we are only interested in storing the digits - CharSequence text = altBtn.getText(); - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - if (Character.isDigit(c)) { - // Convert char to digit and perform click - // Colon is added for you - performClick(c); - } - } - mAmPmState = HRS_24; - } - - updateNumpadStates(); - } - - private boolean is24HourFormat() { - return DateFormat.is24HourFormat(getContext()); - } - - private void inputNumber(CharSequence number) { - mFormattedInput.append(number); - // Add colon if necessary, depending on how many digits entered so far - if (count() == 3) { - // Insert a colon - int digits = getInput(); - if (digits >= 60 && digits < 100 || digits >= 160 && digits < 200) { - // From 060-099 (really only to 095, but might as well go up to 100) - // From 160-199 (really only to 195, but might as well go up to 200), - // time does not exist if colon goes at pos. 1 - mFormattedInput.insert(2, ':'); - // These times only apply to the 24-hour clock, and if we're here, - // the time is not legal yet. So we can't set mAmPmState here for - // either clock. - // The 12-hour clock can only have mAmPmState set when AM/PM are clicked. - } else { - // A valid time exists if colon is at pos. 1 - mFormattedInput.insert(1, ':'); - // We can set mAmPmState here (and not in the above case) because - // the time here is legal in 24-hour clock - if (is24HourFormat()) { - mAmPmState = HRS_24; - } - } - } else if (count() == MAX_DIGITS) { - // Colon needs to move, so remove the colon previously added - mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":")); - mFormattedInput.insert(2, ':'); - - // Time is legal in 24-hour clock - if (is24HourFormat()) { - mAmPmState = HRS_24; - } - } - - // Moved to onClick() - //notifyOnNumberInputListener(mFormattedInput.toString()); - } - - public interface KeyListener extends Numpad.KeyListener { - void onAcceptChanges(); - } - - private void init() { - // Build alternative action buttons - leftAlt = (Button) buildButton(R.layout.numpad_alt_button, 3, 0); - rightAlt = (Button) buildButton(R.layout.numpad_alt_button, 3, 2); - leftAlt.setText(is24HourFormat() ? R.string.left_alt_24hr : R.string.am); - rightAlt.setText(is24HourFormat() ? R.string.right_alt_24hr : R.string.pm); - - leftAlt.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onAltButtonClick(leftAlt); - } - }); - - rightAlt.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onAltButtonClick(rightAlt); - } - }); - - newRow(); - buildBackspace(getChildCount() - 1, 2); - buildCollapse(getChildCount() - 1, 0); - // The FAB is wrapped in a FrameLayout - FrameLayout frame = (FrameLayout) - buildButton(R.layout.numpad_fab, getChildCount() - 1, 1); - fab = (FloatingActionButton) frame.getChildAt(0); - fab.setBackgroundTintList(getResources().getColorStateList(R.color.on_enabled_change_fab)); - - fab.setEnabled(false); - - fab.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (count() == 0) { - // Default time - setTime(0, 0); - } - KeyListener kl; - try { - kl = (KeyListener) getKeyListener(); - } catch (ClassCastException e) { - throw new ClassCastException("Using AlarmNumpad with Numpad.KeyListener instead of AlarmNumpad.KeyListener"); - } catch (NullPointerException e) { - throw new NullPointerException("Numpad's KeyListener is not set"); - } - kl.onAcceptChanges(); - } - }); - - updateNumpadStates(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmUtilsHelper.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmUtilsHelper.java deleted file mode 100644 index e5be364..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmUtilsHelper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import com.philliphsu.clock2.Alarm; - -/** - * Created by Phillip Hsu on 6/3/2016. - */ -@Deprecated -public interface AlarmUtilsHelper { - void scheduleAlarm(Alarm alarm); - void cancelAlarm(Alarm alarm, boolean showToast); -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java deleted file mode 100644 index bca5efa..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java +++ /dev/null @@ -1,593 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.content.Intent; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.annotation.StringRes; -import android.support.design.widget.CoordinatorLayout; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.app.ActionBar; -import android.support.v7.widget.SwitchCompat; -import android.text.format.DateFormat; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.ToggleButton; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.BaseActivity; -import com.philliphsu.clock2.DaysOfWeek; -import com.philliphsu.clock2.R; -import com.philliphsu.clock2.SharedPreferencesHelper; -import com.philliphsu.clock2.alarms.AlarmActivity; -import com.philliphsu.clock2.model.AlarmLoader; -import com.philliphsu.clock2.util.AlarmController; -import com.philliphsu.clock2.util.AlarmUtils; -import com.philliphsu.clock2.util.DateFormatUtils; -import com.philliphsu.clock2.util.LocalBroadcastHelper; - -import java.util.Date; - -import butterknife.Bind; -import butterknife.OnClick; - -import static android.text.format.DateFormat.getTimeFormat; -import static com.philliphsu.clock2.DaysOfWeek.SATURDAY; -import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; -import static com.philliphsu.clock2.util.KeyboardUtils.hideKeyboard; -import static com.philliphsu.clock2.util.Preconditions.checkNotNull; - -/** - * TODO: Consider writing an EditAlarmController that would have an - * AlarmController member variable to manage the states of the alarm. - * The class would have the API for editing the alarm, so move all - * the relevant helper methods from here to there. - */ -@Deprecated -public class EditAlarmActivity extends BaseActivity implements - EditAlarmContract.View, // TODO: Remove @Override from the methods - AlarmUtilsHelper, - SharedPreferencesHelper, - LoaderManager.LoaderCallbacks, - BaseTimePickerDialog.OnTimeSetListener { - private static final String TAG = "EditAlarmActivity"; - private static final String TAG_TIME_PICKER = "time_picker"; - - public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID"; - public static final String EXTRA_MODIFIED_ALARM = "com.philliphsu.clock2.editalarm.extra.MODIFIED_ALARM"; - public static final String EXTRA_IS_DELETING = "com.philliphsu.clock2.editalarm.extra.IS_DELETING"; - - private static final String KEY_INPUT_TIME = "input_time"; - private static final String KEY_ENABLED = "enabled"; - private static final String KEY_CHECKED_DAYS = "checked_days"; - private static final String KEY_LABEL = "label"; - private static final String KEY_RINGTONE_URI = "ringtone"; - private static final String KEY_VIBRATE = "vibrate"; - private static final String KEY_SELECTED_HOUR = "selected_hour"; - private static final String KEY_SELECTED_MINUTE = "selected_minute"; - - private static final int REQUEST_PICK_RINGTONE = 0; - private static final int ID_MENU_ITEM = 0; - - private long mOldAlarmId; - private Uri mSelectedRingtoneUri; - private Alarm mOldAlarm; - private int mSelectedHourOfDay; - private int mSelectedMinute; - private final Intent mResultIntent = new Intent(); - - @Bind(R.id.main_content) CoordinatorLayout mMainContent; - @Bind(R.id.save) Button mSave; - @Bind(R.id.delete) Button mDelete; - @Bind(R.id.on_off) SwitchCompat mSwitch; - @Bind(R.id.input_time) TextView mTimeText; - @Bind({R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6}) - ToggleButton[] mDays; - @Bind(R.id.label) EditText mLabel; - @Bind(R.id.ringtone) Button mRingtone; - @Bind(R.id.vibrate) CheckBox mVibrate; - - @Override - public void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute) { - mSelectedHourOfDay = hourOfDay; - mSelectedMinute = minute; - showTimeText(DateFormatUtils.formatTime(this, hourOfDay, minute)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setWeekDaysText(); - - // Are we recreating this Activity because of a rotation? - // If so, try finding the time picker in our backstack. - BaseTimePickerDialog picker = (BaseTimePickerDialog) - getSupportFragmentManager().findFragmentByTag(TAG_TIME_PICKER); - if (picker != null) { - // Restore the callback - picker.setOnTimeSetListener(this); -// mPicker = picker; - } - - mOldAlarmId = getIntent().getLongExtra(EXTRA_ALARM_ID, -1); - if (mOldAlarmId != -1) { - // getLoaderManager() for support fragments by default returns the - // support version of LoaderManager. However, since this is an Activity, - // we have both the native getLoaderManager() and getSupportLoaderManager(). - // Use the latter to remain consistent with the rest of our current code base. - getSupportLoaderManager().initLoader(0, null, this); - } else { - // Nothing to load, so show default values - showDetails(); - // Show the time picker dialog, if it is not already showing - // AND this is the very first time the activity is being created - if (picker == null && savedInstanceState == null) { - // Wait a bit so the activity and dialog don't show at the same time - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - openTimePicker(); - } - }, 300); - } - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - // Write out any state we wish to save for re-initialization. - // You can either restore this state in onCreate() or by - // overriding onRestoreInstanceState() and restoring it there. - super.onSaveInstanceState(outState); - outState.putString(KEY_INPUT_TIME, mTimeText.getText().toString()); - // This is restored automatically post-rotation - outState.putBoolean(KEY_ENABLED, mSwitch.isChecked()); - // These are restored automatically post-rotation - outState.putBooleanArray(KEY_CHECKED_DAYS, new boolean[] { - mDays[0].isChecked(), - mDays[1].isChecked(), - mDays[2].isChecked(), - mDays[3].isChecked(), - mDays[4].isChecked(), - mDays[5].isChecked(), - mDays[6].isChecked() - }); - // This is restored automatically post-rotation - outState.putString(KEY_LABEL, mLabel.getText().toString()); - outState.putParcelable(KEY_RINGTONE_URI, mSelectedRingtoneUri); - // This is restored automatically post-rotation - outState.putBoolean(KEY_VIBRATE, mVibrate.isChecked()); - outState.putInt(KEY_SELECTED_HOUR, mSelectedHourOfDay); - outState.putInt(KEY_SELECTED_MINUTE, mSelectedMinute); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - // For now, restore the previous state here instead of in onCreate() - // because it's easier than refactoring the code over there. - super.onRestoreInstanceState(savedInstanceState); - // No null-check on the given Bundle is necessary, - // because onSaveInstanceState() works with a non-null - // Bundle, even in the default implementation. - if (savedInstanceState.containsKey(KEY_INPUT_TIME) - //&& savedInstanceState.containsKey(KEY_LABEL) - && savedInstanceState.containsKey(KEY_RINGTONE_URI)) { - // Make sure we actually saved something, or else we'd get - // a null and we'll end up clearing the hints... - mTimeText.setText(savedInstanceState.getString(KEY_INPUT_TIME)); -// mLabel.setText(savedInstanceState.getString(KEY_LABEL)); - mSelectedRingtoneUri = savedInstanceState.getParcelable(KEY_RINGTONE_URI); - // ...this, however, would throw an NPE because - // we'd be accessing a null Ringtone. - updateRingtoneButtonText(); - } - mSelectedHourOfDay = savedInstanceState.getInt(KEY_SELECTED_HOUR); - mSelectedMinute = savedInstanceState.getInt(KEY_SELECTED_MINUTE); - // TODO: Manually restore the states of the "auto-restoring" widgets. - // In onCreate(), we will call showDetails(). - // You only witnessed the auto-restoring for a blank Alarm, where - // the impl of showDetails() is pretty bare. If we have an actual - // Alarm, showDetails() could very well change the values displayed - // by those widgets based on that Alarm's values, but not based on - // any unsaved changes that may have occurred to the widgets previously. - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // TODO: Snooze menu item when alarm is ringing. - if (mOldAlarm != null && mOldAlarm.isEnabled()) { - int hoursBeforeUpcoming = getInt(R.string.key_notify_me_of_upcoming_alarms, 2); - // TODO: Schedule task with handler to show the menu item when it is time. - // Handler is fine because the task only needs to be done if the activity - // is being viewed. (I think) if the process of this - // app is killed, then the handler is also killed. - if ((mOldAlarm.ringsWithinHours(hoursBeforeUpcoming))) { - showCanDismissNow(); - } else if (mOldAlarm.isSnoozed()) { - showSnoozed(new Date(mOldAlarm.snoozingUntil())); - } - } - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_dismiss_now: - case R.id.action_done_snoozing: - cancelAlarm(checkNotNull(mOldAlarm), true); - // cancelAlarm() should have turned off this alarm if appropriate - showEnabled(mOldAlarm.isEnabled()); - item.setVisible(false); - // This only matters for case R.id.action_done_snoozing. - // It won't hurt to call this for case R.id.action_dismiss_now. - getSupportActionBar().setDisplayShowTitleEnabled(false); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // Since this Activity doesn't host fragments, not necessary? - //super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_PICK_RINGTONE && resultCode == RESULT_OK) { - mSelectedRingtoneUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); - updateRingtoneButtonText(); - } - } - - @Override - protected int layoutResId() { - return R.layout.activity_edit_alarm_v2; - } - - @Override - protected int menuResId() { - return R.menu.menu_edit_alarm; - } - - @OnClick(R.id.save) - void save() { - Alarm alarm = Alarm.builder() - .hour(mSelectedHourOfDay) - .minutes(mSelectedMinute) - .ringtone(getRingtone()) - .label(getLabel()) - .vibrates(vibrates()) - .build(); - alarm.setEnabled(isEnabled()); - for (int i = SUNDAY; i <= SATURDAY; i++) { - alarm.setRecurring(i, isRecurringDay(i)); - } - - if (mOldAlarm != null) { - if (mOldAlarm.isEnabled()) { - Log.d(TAG, "Cancelling old alarm first"); - cancelAlarm(mOldAlarm, false); - } - alarm.setId(mOldAlarm.id()); - mResultIntent.putExtra(EXTRA_IS_DELETING, false); - } - mResultIntent.putExtra(EXTRA_MODIFIED_ALARM, alarm); - - // The reason we don't schedule the alarm here is AlarmUtils - // will attempt to retrieve the specified alarm - // from the database; however, the alarm hasn't yet - // been added to the database at this point. - - finish(); - } - - // TODO: Private accessor - @OnClick(R.id.delete) - void delete() { - if (mOldAlarm != null) { - if (mOldAlarm.isEnabled()) { - cancelAlarm(mOldAlarm, false); - // Re-enable in case this is restored so - // the alarm can be scheduled again. This - // change is saved to the db if the alarm - // is restored (re-inserting into to the db). - mOldAlarm.setEnabled(true); - } - mResultIntent.putExtra(EXTRA_IS_DELETING, true); - mResultIntent.putExtra(EXTRA_MODIFIED_ALARM, mOldAlarm); - } - finish(); - } - - @Override - public void finish() { - if (mResultIntent.getExtras() != null) { - setResult(RESULT_OK, mResultIntent); - } - super.finish(); - } - - @OnClick(R.id.input_time) - void openTimePicker() { - // Close the keyboard first, or else our dialog will be screwed up. - // If not open, this does nothing. - hideKeyboard(this); // This is only important for BottomSheetDialogs! - // Create a new instance each time we want to show the dialog. - // 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(this).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(this)); - } - dialog.show(getSupportFragmentManager(), TAG_TIME_PICKER); - } - - private void setWeekDaysText() { - for (int i = 0; i < mDays.length; i++) { - int weekDay = DaysOfWeek.getInstance(this).weekDayAt(i); - String label = DaysOfWeek.getLabel(weekDay); - mDays[i].setTextOn(label); - mDays[i].setTextOff(label); - mDays[i].setChecked(mDays[i].isChecked()); // force update the text, otherwise it won't be shown - } - } - - private void updateRingtoneButtonText() { - mRingtone.setText(RingtoneManager.getRingtone(this, mSelectedRingtoneUri).getTitle(this)); - } - - @Override - public void showRecurringDays(int weekDay, boolean recurs) { - // What position in the week is this day located at? - int at = DaysOfWeek.getInstance(this).positionOf(weekDay); - // Toggle the button that corresponds to this day - mDays[at].setChecked(recurs); - } - - @Override - public void showRingtone(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. - if (null == ringtone || ringtone.isEmpty()) { - mSelectedRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); - } else { - mSelectedRingtoneUri = Uri.parse(ringtone); - } - updateRingtoneButtonText(); - } - - @Override - public void showVibrates(boolean vibrates) { - mVibrate.setChecked(vibrates); - } - - @Deprecated - @Override - public void showEditorClosed() { - finish(); - } - - @Override - public int getHour() { - return mSelectedHourOfDay; - } - - @Override - public int getMinutes() { - return mSelectedMinute; - } - - @Override - public boolean isEnabled() { - return mSwitch.isChecked(); - } - - @Override - public boolean isRecurringDay(int weekDay) { - // What position in the week is this day located at? - int pos = DaysOfWeek.getInstance(this).positionOf(weekDay); - // Return the state of this day according to its button - return mDays[pos].isChecked(); - } - - @Override - public String getLabel() { - return mLabel.getText().toString(); - } - - @Override - public String getRingtone() { - return mSelectedRingtoneUri.toString(); - } - - @Override - public boolean vibrates() { - return mVibrate.isChecked(); - } - - @Override - public void showTime(int hour, int minutes) { - // TODO: Delete - showTimeText(DateFormatUtils.formatTime(this, hour, minutes)); - } - - @Override - public void showLabel(String label) { - mLabel.setText(label); - } - - @Override - public void showEnabled(boolean enabled) { - mSwitch.setChecked(enabled); - } - - @Override - public void showNumpad(boolean show) { - // TODO: Delete - } - - @Override - public void showCanDismissNow() { - Menu menu = checkNotNull(getMenu()); - MenuItem item = menu.findItem(R.id.action_dismiss_now); - if (!item.isVisible()) { - item.setVisible(true); - menu.findItem(R.id.action_done_snoozing).setVisible(false); - } - } - - @Override - public void showSnoozed(Date snoozingUntilMillis) { - Menu menu = checkNotNull(getMenu()); - MenuItem item = menu.findItem(R.id.action_done_snoozing); - if (!item.isVisible()) { - item.setVisible(true); - menu.findItem(R.id.action_dismiss_now).setVisible(false); - } - String title = getString(R.string.title_snoozing_until, - getTimeFormat(this).format(snoozingUntilMillis)); - ActionBar ab = getSupportActionBar(); - ab.setDisplayShowTitleEnabled(true); - ab.setTitle(title); - } - - @Deprecated // TODO: Delete in favor of setDefaultTime() - @Override - public void setTimeTextHint() { - if (DateFormat.is24HourFormat(this)) { - mTimeText.setText(R.string.default_alarm_time_24h); - } else { - showTimeText(getString(R.string.default_alarm_time_12h)); - } - } - - private void setDefaultTime() { - mSelectedHourOfDay = 0; - mSelectedMinute = 0; - // TODO: We could simplify this to just showTimeText() with the only difference being - // the string that is passed in. - if (DateFormat.is24HourFormat(this)) { - mTimeText.setText(R.string.default_alarm_time_24h); - } else { - showTimeText(getString(R.string.default_alarm_time_12h)); - } - } - - @Override - public void showTimeText(String formattedInput) { - TimeTextUtils.setText(formattedInput, mTimeText); - } - - @Deprecated - @Override - public void showTimeTextPostBackspace(String newStr) { - // TODO: Remove - } - - @Override - @OnClick(R.id.ringtone) - public void showRingtonePickerDialog() { - Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); - intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM) - .putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false) - // The ringtone to show as selected when the dialog is opened - .putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, mSelectedRingtoneUri) - // Whether to show "Default" item in the list - .putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false); - // The ringtone that plays when default option is selected - //.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, DEFAULT_TONE); - startActivityForResult(intent, REQUEST_PICK_RINGTONE); - } - - @Override - public void showTimeTextFocused(boolean focused) { - if (focused) { - mTimeText.requestFocus(); - // Move cursor to end - } else { - mTimeText.clearFocus(); - } - } - - // TODO: Delete this - @Deprecated - @Override - public void scheduleAlarm(Alarm alarm) { - //AlarmUtils.scheduleAlarm(this, alarm, true); - } - - @Override - public void cancelAlarm(Alarm alarm, boolean showToast) { - new AlarmController(this, mMainContent).cancelAlarm(alarm, true); - if (AlarmActivity.isAlive()) { - LocalBroadcastHelper.sendBroadcast(this, AlarmActivity.ACTION_FINISH); - } - } - - @Override - public int getInt(@StringRes int key, int defaultValue) { - return AlarmUtils.readPreference(this, key, defaultValue); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new AlarmLoader(this, mOldAlarmId); - } - - @Override - public void onLoadFinished(Loader loader, Alarm data) { - mOldAlarm = data; - showDetails(); - } - - @Override - public void onLoaderReset(Loader loader) { - // nothing to reset - } - - // TODO: Privatize access of each method called here. - private void showDetails() { - if (mOldAlarm != null) { - showTime(mOldAlarm.hour(), mOldAlarm.minutes()); - showEnabled(mOldAlarm.isEnabled()); - for (int i = SUNDAY; i <= SATURDAY; i++) { - showRecurringDays(i, mOldAlarm.isRecurring(i)); - } - showLabel(mOldAlarm.label()); - showRingtone(mOldAlarm.ringtone()); - showVibrates(mOldAlarm.vibrates()); - showTimeTextFocused(false); - } else { - // TODO default values - setDefaultTime(); - showTimeTextFocused(true); - showRingtone(""); // gets default ringtone - showEnabled(true); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmContract.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmContract.java deleted file mode 100644 index 59eb7ca..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmContract.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -/** - * Created by Phillip Hsu on 6/2/2016. - * - * TODO: Consider NOT extending from AlarmContract. Instead, define the methods - * specific to this interface. Make a base implementation class of the base - * AlarmContract. When you create an implementation class of the more specific - * interface, all you need to do is implement the more specific interface. - * The base impl class will already implement the base interface methods. - * That way, each concrete interface impl doesn't need to implement the - * base interface methods again and again. - */ -@Deprecated -public interface EditAlarmContract { - - interface View extends AlarmContract.View { - void showTime(int hour, int minutes); - void showRecurringDays(int weekDay, boolean recurs); - void showRingtone(String ringtone); - void showVibrates(boolean vibrates); - void showEditorClosed(); - void showNumpad(boolean show); - void showRingtonePickerDialog(); - void setTimeTextHint(); - void showTimeText(String timeText); - void showTimeTextPostBackspace(String newStr); - void showTimeTextFocused(boolean focused); - int getHour(); - int getMinutes(); - boolean isEnabled(); - boolean isRecurringDay(int weekDay); - String getLabel(); - String getRingtone(); - boolean vibrates(); - } - - interface Presenter extends AlarmContract.Presenter { - void loadAlarm(long id); - void save(); - void delete(); - void showNumpad(); - void hideNumpad(); - // not sure - void onBackspace(String newStr); - void acceptNumpadChanges(); - void onPrepareOptionsMenu(); - void openRingtonePickerDialog(); - void setTimeTextHint(); - void onNumberInput(String formattedInput); - void focusTimeText(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmPresenter.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmPresenter.java deleted file mode 100644 index 1a67da5..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmPresenter.java +++ /dev/null @@ -1,213 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.R; -import com.philliphsu.clock2.SharedPreferencesHelper; -import com.philliphsu.clock2.model.Repository; - -import java.util.Date; - -import static com.philliphsu.clock2.DaysOfWeek.SATURDAY; -import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; -import static com.philliphsu.clock2.util.Preconditions.checkNotNull; - -/** - * Created by Phillip Hsu on 6/3/2016. - */ -@Deprecated -public class EditAlarmPresenter implements EditAlarmContract.Presenter { - private static final String TAG = "EditAlarmPresenter"; - - @NonNull private final EditAlarmContract.View mView; - @NonNull private final Repository mRepository; - @NonNull private final AlarmUtilsHelper mAlarmUtilsHelper; - @NonNull private final SharedPreferencesHelper mSharedPreferencesHelper; - @Nullable private Alarm mAlarm; - - @Deprecated - public EditAlarmPresenter(@NonNull EditAlarmContract.View view, - @NonNull Repository repository, - @NonNull AlarmUtilsHelper helper, - @NonNull SharedPreferencesHelper sharedPreferencesHelper) { - mView = view; - mRepository = repository; - mAlarmUtilsHelper = helper; - mSharedPreferencesHelper = sharedPreferencesHelper; - } - - @Deprecated - @Override - public void loadAlarm(long alarmId) { - // Can't load alarm in ctor because showDetails() calls - // showTime(), which calls setTime() on the numpad, which - // fires onNumberInput() events, which routes to the presenter, - // which would not be initialized yet because we still haven't - // returned from the ctor. - mAlarm = alarmId > -1 ? mRepository.getItem(alarmId) : null; - showDetails(); - } - - @Deprecated - @Override - public void save() { - int hour; - int minutes; - try { - hour = mView.getHour(); - minutes = mView.getMinutes(); - } catch (IllegalStateException e) { - Log.e(TAG, e.getMessage()); - return; - } - - Alarm a = Alarm.builder() - .hour(hour) - .minutes(minutes) - .ringtone(mView.getRingtone()) - .label(mView.getLabel()) - .vibrates(mView.vibrates()) - .build(); - a.setEnabled(mView.isEnabled()); - for (int i = SUNDAY; i <= SATURDAY; i++) { - a.setRecurring(i, mView.isRecurringDay(i)); - } - - if (mAlarm != null) { - if (mAlarm.isEnabled()) { - Log.d(TAG, "Cancelling old alarm first"); - mAlarmUtilsHelper.cancelAlarm(mAlarm, false); - } - mRepository.updateItem(mAlarm, a); - } else { - mRepository.addItem(a); - } - - if (a.isEnabled()) { - mAlarmUtilsHelper.scheduleAlarm(a); - } - - mView.showEditorClosed(); - } - - @Deprecated - @Override - public void delete() { - if (mAlarm != null) { - if (mAlarm.isEnabled()) { - mAlarmUtilsHelper.cancelAlarm(mAlarm, false); - } - mRepository.deleteItem(mAlarm); - } - mView.showEditorClosed(); - } - - @Deprecated - @Override - public void dismissNow() { - mAlarmUtilsHelper.cancelAlarm(checkNotNull(mAlarm), true); - // cancelAlarm() should have turned off this alarm if appropriate - mView.showEnabled(mAlarm.isEnabled()); - } - - @Deprecated - @Override - public void stopSnoozing() { - dismissNow(); // MUST be first, see AlarmUtils.notifyUpcomingAlarmIntent() - // AlarmUtils.cancelAlarm() does this for you if snoozed - /* - mAlarm.stopSnoozing(); // TOneverDO: before dismissNow() - mRepository.saveItems(); - */ - } - - @Deprecated - @Override - public void showNumpad() { - mView.showNumpad(true); - } - - @Deprecated - @Override - public void hideNumpad() { - mView.showNumpad(false); - } - - @Deprecated - @Override - public void onBackspace(String newStr) { - mView.showTimeTextPostBackspace(newStr); - } - - @Deprecated - @Override - public void acceptNumpadChanges() { - mView.showNumpad(false); - mView.showEnabled(true); - } - - @Deprecated - @Override - public void onPrepareOptionsMenu() { - if (mAlarm != null && mAlarm.isEnabled()) { - int hoursBeforeUpcoming = mSharedPreferencesHelper.getInt(R.string.key_notify_me_of_upcoming_alarms, 2); - // TODO: Schedule task with handler to show the menu item when it is time. Handler is fine because - // the task only needs to be done if the activity is being viewed. (I think) if the process of this - // app is killed, then the handler is also killed. - if ((mAlarm.ringsWithinHours(hoursBeforeUpcoming))) { - mView.showCanDismissNow(); - } else if (mAlarm.isSnoozed()) { - mView.showSnoozed(new Date(mAlarm.snoozingUntil())); - } - } - } - - @Deprecated - @Override - public void openRingtonePickerDialog() { - mView.showRingtonePickerDialog(); - } - - @Deprecated - @Override - public void setTimeTextHint() { - mView.setTimeTextHint(); - } - - @Deprecated - @Override - public void onNumberInput(String formattedInput) { - mView.showTimeText(formattedInput); - } - - @Deprecated - @Override - public void focusTimeText() { - mView.showTimeTextFocused(true); - } - - @Deprecated - private void showDetails() { - if (mAlarm != null) { - mView.showTime(mAlarm.hour(), mAlarm.minutes()); - mView.showEnabled(mAlarm.isEnabled()); - for (int i = SUNDAY; i <= SATURDAY; i++) { - mView.showRecurringDays(i, mAlarm.isRecurring(i)); - } - mView.showLabel(mAlarm.label()); - mView.showRingtone(mAlarm.ringtone()); - mView.showVibrates(mAlarm.vibrates()); - // Editing so don't show - mView.showNumpad(false); - mView.showTimeTextFocused(false); - } else { - // TODO default values - mView.showTimeTextFocused(true); - mView.showRingtone(""); // gets default ringtone - mView.showNumpad(true); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java b/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java index 63526c5..55e330c 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/GridLayoutNumpad.java @@ -23,9 +23,6 @@ import butterknife.OnClick; * Created by Phillip Hsu on 7/12/2016. * * Successor to the Numpad class that was based on TableLayout. - * Unlike Numpad, this class only manages the logic for number button clicks - * and not the backspace button. However, we do provide an API for removing - * digits from the input. * * TODO: Is NumpadTimePicker the only subclass? If so, why do we need this * superclass? If we move the contents of this class to NumpadTimePicker, diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java deleted file mode 100644 index 2331364..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumbersGridView.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.content.Context; -import android.support.v7.widget.GridLayout; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.view.View; -import android.widget.TextView; - -import com.philliphsu.clock2.R; - -/** - * Created by Phillip Hsu on 7/21/2016. - */ -@Deprecated -public class NumbersGridView extends GridLayout { - private static final String TAG = "NumberGrid"; - private static final int COLUMNS = 3; - -// private CircularIndicatorSetter mIndicatorSetter; - private OnNumberSelectedListener mSelectionListener; - // TODO: Since we plan to dynamically clear and inflate children into this - // parent to represent "pages", this seems useless? Since each page has at - // most one selection, and at any given time, this GridLayout can only show - // one page at a time. Instead, when the onNumberSelected() is fired, the - // hosting dialog should keep a reference to the returned number. It is up - // to the dialog to deduce what time field the number represents, probably - // with an int flag that indicates what "page" this GridLayout is displaying. - private int mSelection; - - public interface OnNumberSelectedListener { - void onNumberSelected(int number); - } - - public NumbersGridView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public NumbersGridView(Context context) { - super(context); - init(); - } - - public void setOnNumberSelectedListener(OnNumberSelectedListener onNumberSelectedListener) { - mSelectionListener = onNumberSelectedListener; - } - -// /*package*/ void setTheme(Context context, boolean themeDark) { -// for (int i = 0; i < getChildCount(); i++) { -// View view = getChildAt(i); -// if (view instanceof TextViewWithCircularIndicator) { -// TextViewWithCircularIndicator text = (TextViewWithCircularIndicator) getChildAt(i); -// text.setTheme(context, themeDark); -// } -// } -// } - - public int getSelection() { - return mSelection; - } - - public void setSelection(int value) { - mSelection = value; -// for (int i = 0; i < getChildCount(); i++) { -// View v = getChildAt(i); -// if (v instanceof TextViewWithCircularIndicator) { -// TextViewWithCircularIndicator text = (TextViewWithCircularIndicator) v; -// // parseInt() strips out leading zeroes -// int num = Integer.parseInt(text.getText().toString()); -// if (value == num) { -// mIndicatorSetter.setIndicator(text); -// break; -// } -// } else { -// // We have reached a non-numeric button, i.e. the minute tuners, unless you have -// // other non-numeric buttons as well. This means we iterated through all numeric -// // buttons, but this value is not one of the preset values. -// // Clear the indicator from the default selection. -// mIndicatorSetter.setIndicator(null); -// break; -// } -// } - } - - /** - * Set the numbers to be displayed in this grid. - */ - public void setNumbers(int[] numbers) { - // TODO: This method isn't applicable to the 24 Hour grid.. consider creating a subclass - // just for 24 hour values? Or just use a regular GridLayout in the dialog's layout - // as the container for whatever arbitrary children you want? - // TODO: Depending on the user's clock system, there will be different logic to toggle - // between "pages". If the user uses 12-hour time, then the same NumberGrid can be reused - // for both pages, and you can use this method to replace the texts. Otherwise, you have to - // remove all of the 24-hour value items from this grid and inflate the minutes layout - // into this grid. Find an elegant solution to implement this logic. - setNumbers(numbers, false); - } - - public void setNumbers(int[] numbers, boolean zeroPadSingleDigits) { - if (numbers != null) { - int i = 0; - View child; - while ((child = getChildAt(i)) instanceof TextView/*TODO: TextViewWithCircularIndicator*/) { - String s = zeroPadSingleDigits - ? String.format("%02d", numbers[i]) - : String.valueOf(numbers[i]); - ((TextView) child).setText(s); - child.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setNumbers(new int[] {0,5,10,15,20,25,30,35,40,45,50,55}, true); - inflate(getContext(), R.layout.content_minutes_grid, NumbersGridView.this); - } - }); - i++; - } - } - } - - /** - * Final because this is implemented for the grid of numbers. If subclasses need their own - * click listeners for non-numeric buttons, they should set new OnClickListeners on those buttons. - */ -// @Override -// public final void onClick(View v) { -// TextViewWithCircularIndicator view = (TextViewWithCircularIndicator) v; -// String text = view.getText().toString(); -// int number = Integer.parseInt(text); -// mSelection = number; -// fireOnNumberSelectedEvent(number); -// mIndicatorSetter.setIndicator(view); -// } - - protected void fireOnNumberSelectedEvent(int number) { - if (mSelectionListener != null) - mSelectionListener.onNumberSelected(number); - } - - private void init() { -// setAlignmentMode(ALIGN_BOUNDS); - setColumnCount(COLUMNS); - // When we initialize, display the hour values "page". - boolean is24HourMode = DateFormat.is24HourFormat(getContext()); - int layout = is24HourMode - ? R.layout.content_24h_number_grid - : R.layout.content_hours_grid; - inflate(getContext(), layout, this); - if (!is24HourMode) { - setNumbers(new int[] {1,2,3,4,5,6,7,8,9,10,11,12}); - } -// ButterKnife.bind(this); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerController.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerController.java deleted file mode 100644 index 0cb1e23..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerController.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.view.View; - -/** - * Created by Phillip Hsu on 7/20/2016. - * - * TODO: This class has NOT been properly written yet. Consider moving all button state code - * from the Numpad classes to here. - * TODO: We might need to write a setAmPmState() method in NumpadTimePicker. - * - * NumpadTimePickerDialog would store a reference to this controller and use this to - * update the states of the various buttons in its layout. Currently, this class has - * ported over updateNumpadStates() and its related methods from NumpadTimePicker. - */ -public class NumpadTimePickerController { - - private NumpadTimePicker mPicker; - private View mConfirmSelectionButton; - private View mLeftAltButton; - private View mRightAltButton; - private boolean mIs24HourMode; - -/* - public NumpadTimePickerController(NumpadTimePicker picker, View confirmSelectionButton, - View leftAltButton, View rightAltButton, boolean is24HourMode) { - mPicker = picker; - mConfirmSelectionButton = confirmSelectionButton; - mLeftAltButton = leftAltButton; - mRightAltButton = rightAltButton; - mIs24HourMode = is24HourMode; - } - - public void updateNumpadStates() { - // TOneverDO: after updateNumberKeysStates(), esp. if clock is 12-hour, - // because it calls mPicker.enable(0, 0), which checks if the alt buttons have been - // disabled as well before firing the onInputDisabled(). - updateAltButtonStates(); - - updateBackspaceState(); - updateNumberKeysStates(); - updateFabState(); - } - - public void updateFabState() { - mConfirmSelectionButton.setEnabled(mPicker.checkTimeValid()); - } - - public void updateBackspaceState() { - mPicker.setBackspaceEnabled(mPicker.count() > 0); - } - - public void updateAltButtonStates() { - if (mPicker.count() == 0) { - // No input, no access! - mLeftAltButton.setEnabled(false); - mRightAltButton.setEnabled(false); - } else if (mPicker.count() == 1) { - // Any of 0-9 inputted, always have access in either clock. - mLeftAltButton.setEnabled(true); - mRightAltButton.setEnabled(true); - } else if (mPicker.count() == 2) { - // Any 2 digits that make a valid hour for either clock are eligible for access - int time = mPicker.getInput(); - boolean validTwoDigitHour = mIs24HourMode ? time <= 23 : time >= 10 && time <= 12; - mLeftAltButton.setEnabled(validTwoDigitHour); - mRightAltButton.setEnabled(validTwoDigitHour); - } else if (mPicker.count() == 3) { - if (mIs24HourMode) { - // For the 24-hour clock, no access at all because - // two more digits (00 or 30) cannot be added to 3 digits. - mLeftAltButton.setEnabled(false); - mRightAltButton.setEnabled(false); - } else { - // True for any 3 digits, if AM/PM not already entered - boolean enabled = mAmPmState == UNSPECIFIED; - mLeftAltButton.setEnabled(enabled); - mRightAltButton.setEnabled(enabled); - } - } else if (mPicker.count() == mPicker.capacity()) { - // If all 4 digits are filled in, the 24-hour clock has absolutely - // no need for the alt buttons. However, The 12-hour clock has - // complete need of them, if not already used. - boolean enabled = !mIs24HourMode && mAmPmState == UNSPECIFIED; - mLeftAltButton.setEnabled(enabled); - mRightAltButton.setEnabled(enabled); - } - } - - public void updateNumberKeysStates() { - int cap = 10; // number of buttons - boolean is24hours = mIs24HourMode; - - if (mPicker.count() == 0) { - mPicker.enable(is24hours ? 0 : 1, cap); - return; - } else if (mPicker.count() == mPicker.capacity()) { - mPicker.enable(0, 0); - return; - } - - int time = mPicker.getInput(); - if (is24hours) { - if (mPicker.count() == 1) { - mPicker.enable(0, time < 2 ? cap : 6); - } else if (mPicker.count() == 2) { - mPicker.enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 6); - } else if (mPicker.count() == 3) { - if (time >= 236) { - mPicker.enable(0, 0); - } else { - mPicker.enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } else { - if (mPicker.count() == 1) { - if (time == 0) { - throw new IllegalStateException("12-hr format, zeroth digit = 0?"); - } else { - mPicker.enable(0, 6); - } - } else if (mPicker.count() == 2 || mPicker.count() == 3) { - if (time >= 126) { - mPicker.enable(0, 0); - } else { - if (time >= 100 && time <= 125 && mAmPmState != UNSPECIFIED) { - // Could legally input fourth digit, if not for the am/pm state already set - mPicker.enable(0, 0); - } else { - mPicker.enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0); - } - } - } - } - } -*/ - -} diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java index b6d9412..ad56aa8 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/NumpadTimePickerDialog.java @@ -9,6 +9,7 @@ import android.widget.TextView; import com.philliphsu.clock2.R; import com.philliphsu.clock2.aospdatetimepicker.Utils; +import com.philliphsu.clock2.util.TimeTextUtils; import butterknife.Bind; import butterknife.OnClick; diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/TimePicker.java b/app/src/main/java/com/philliphsu/clock2/editalarm/TimePicker.java deleted file mode 100644 index 9da3a55..0000000 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/TimePicker.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.philliphsu.clock2.editalarm; - -import android.view.ViewGroup; - -/** - * Created by Phillip Hsu on 7/12/2016. - * - * The callback interface used to indicate the user is done filling in - * the time (they clicked on the 'Set' button). - */ -public interface TimePicker { - int hourOfDay(); - int minute(); - - /** - * The callback interface used to indicate the user is done filling in - * the time (they clicked on the 'Set' button). - */ - interface OnTimeSetListener { - /** - * @param viewGroup The view associated with this listener. - * @param hourOfDay The hour that was set. - * @param minute The minute that was set. - */ - // TODO: Consider changing VG param to TimePicker - void onTimeSet(ViewGroup viewGroup, int hourOfDay, int minute); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmIoHelper.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmIoHelper.java deleted file mode 100644 index ff6c0b9..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmIoHelper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.support.annotation.NonNull; - -import com.philliphsu.clock2.Alarm; - -import org.json.JSONObject; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -@Deprecated -public class AlarmIoHelper extends JsonIoHelper { - private static final String FILENAME = "alarms.json"; - - public AlarmIoHelper(@NonNull Context context) { - super(context, FILENAME); - } - - @Override - protected Alarm newItem(@NonNull JSONObject jsonObject) { - return Alarm.create(jsonObject); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java deleted file mode 100644 index 4c22470..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; - -import com.philliphsu.clock2.Alarm; - -/** - * Created by Phillip Hsu on 6/30/2016. - */ -public class AlarmLoader extends DataLoader { - - private long mAlarmId; - - // TODO: Consider writing a super ctor that has the id param, so - // subclasses don't need to write their own. - public AlarmLoader(Context context, long alarmId) { - super(context); - mAlarmId = alarmId; - } - - @Override - public Alarm loadInBackground() { - return new AlarmsTableManager(getContext()).queryItem(mAlarmId).getItem(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmsRepository.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmsRepository.java deleted file mode 100644 index d9597ec..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmsRepository.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.util.Log; - -import com.philliphsu.clock2.Alarm; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -@Deprecated -public class AlarmsRepository extends BaseRepository { - private static final String TAG = "AlarmsRepository"; - // Singleton, so this is the sole instance for the lifetime - // of the application; thus, instance fields do not need to - // be declared static because they are already associated with - // this single instance. Since no other instance can exist, - // any member fields are effectively class fields. - // ** - // Can't be final, otherwise you'd need to instantiate inline - // or in static initializer, but ctor requires Context so you - // can't do that either. - private static AlarmsRepository sRepo; - - private AlarmsRepository(@NonNull Context context) { - super(context, new AlarmIoHelper(context)); - } - - public static AlarmsRepository getInstance(@NonNull Context context) { - if (null == sRepo) { - Log.d(TAG, "Loading AlarmsRepository for the first time"); - sRepo = new AlarmsRepository(context); - } - return sRepo; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/BaseRepository.java b/app/src/main/java/com/philliphsu/clock2/model/BaseRepository.java deleted file mode 100644 index 3e585b4..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/BaseRepository.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -@Deprecated -public abstract class BaseRepository implements Repository { - private static final String TAG = "BaseRepository"; - // Cannot do this! Multiple classes will extend from this, - // so this "singleton" would be a class property for all of them. - //private static BaseRepositoryV2 sInstance; - - // Never used since subclasses provide the ioHelper, but I think - // the intention is that we hold onto the global context so this - // never gets GCed for the lifetime of the app. - @NonNull private final Context mContext; - @NonNull private final List mItems; // TODO: Consider ArrayMap? - @NonNull private final JsonIoHelper mIoHelper; - - // TODO: Test that the callbacks work. - private DataObserver mDataObserver; - - // We could use T but since it's already defined, we should avoid - // the needless confusion and use a different type param. You won't - // be able to refer to the type that T resolves to anyway. - public interface DataObserver { - void onItemAdded(T2 item); - void onItemDeleted(T2 item); - void onItemUpdated(T2 oldItem, T2 newItem); - } - - /*package-private*/ BaseRepository(@NonNull Context context, - @NonNull JsonIoHelper ioHelper) { - Log.d(TAG, "BaseRepositoryV2 initialized"); - mContext = context.getApplicationContext(); - mIoHelper = ioHelper; // MUST precede loading items - mItems = loadItems(); // TOneverDO: move this elsewhere - } - - @Override @NonNull - public List getItems() { - return Collections.unmodifiableList(mItems); - } - - @Nullable - @Override - public T getItem(long id) { - for (T item : getItems()) - if (item.id() == id) - return item; - return null; - } - - @Override - public final void addItem(@NonNull T item) { - Log.d(TAG, "New item added"); - mItems.add(item); - mDataObserver.onItemAdded(item); // TODO: Set StopwatchView as DataObserver - saveItems(); - } - - @Override - public final void deleteItem(@NonNull T item) { - if (!mItems.remove(item)) { - Log.e(TAG, "Cannot remove an item that is not in the list"); - } else { - mDataObserver.onItemDeleted(item); // TODO: Set StopwatchView as DataObserver - saveItems(); - } - } - - @Override - public final void updateItem(@NonNull T item1, @NonNull T item2) { - // TODO: Won't work unless objects are immutable, so item1 - // can't change and thus its index will never change - // ** - // Actually, since the items come from this list, - // modifications to items will directly "propagate". - // In the process, the index of that modified item - // has not changed. If that's the case, there really - // isn't any point for an update method, especially - // since item2 would be unnecessary and won't even need - // to be used. - mItems.set(mItems.indexOf(item1), item2); - mDataObserver.onItemUpdated(item1, item2); // TODO: Set StopwatchView as DataObserver - saveItems(); - } - - @Override - public final boolean saveItems() { - try { - mIoHelper.saveItems(mItems); - Log.d(TAG, "Saved items to file"); - return true; - } catch (IOException e) { - Log.e(TAG, "Error writing items to file: " + e); - return false; - } - } - - @Override - public final void clear() { - mItems.clear(); - saveItems(); - } - - public final void registerDataObserver(@NonNull DataObserver observer) { - mDataObserver = observer; - } - - // TODO: Do we need to call this? - public final void unregisterDataObserver() { - mDataObserver = null; - } - - @NonNull - private List loadItems() { - try { - return mIoHelper.loadItems(); - } catch (IOException e) { - Log.e(TAG, "Error loading items from file: " + e); - return new ArrayList<>(); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/DataLoader.java b/app/src/main/java/com/philliphsu/clock2/model/DataLoader.java deleted file mode 100644 index bd33bb4..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/DataLoader.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; - -/** - * Created by Phillip Hsu on 6/30/2016. - */ -// TODO: Consider adding a DatabaseTableManager type param, so we can then -// implement loadInBackground for subclasses. You would, however, need to write -// an abstract method getTableManager() that subclasses implement for us. -public abstract class DataLoader extends AsyncTaskLoader { - - private D mData; - - public DataLoader(Context context) { - super(context); - } - - @Override - protected void onStartLoading() { - if (mData != null) { - deliverResult(mData); - } else { - forceLoad(); - } - } - - @Override - public void deliverResult(D data) { - mData = data; - if (isStarted()) { - super.deliverResult(data); - } - } - -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/JsonIoHelper.java b/app/src/main/java/com/philliphsu/clock2/model/JsonIoHelper.java deleted file mode 100644 index 5dce01a..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/JsonIoHelper.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.support.annotation.NonNull; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -@Deprecated -public abstract class JsonIoHelper { - @NonNull private final Context mContext; - @NonNull private final String mFilename; - - public JsonIoHelper(@NonNull Context context, - @NonNull String filename) { - mContext = context.getApplicationContext(); - mFilename = filename; - } - - protected abstract T newItem(@NonNull JSONObject jsonObject); - - public final List loadItems() throws IOException { - ArrayList items = new ArrayList<>(); - BufferedReader reader = null; - try { - // Opens the file in a FileInputStream for byte-reading - InputStream in = mContext.openFileInput(mFilename); - // Use an InputStreamReader to convert bytes to characters. A BufferedReader wraps the - // existing Reader and provides a buffer (a cache) for storing the characters. - // From https://docs.oracle.com/javase/7/docs/api/java/io/BufferedReader.html: - // "In general, each read request made of a Reader causes a corresponding read request - // to be made of the underlying character or byte stream. It is therefore advisable to - // wrap a BufferedReader around any Reader whose read() operations may be costly, such as - // FileReaders and InputStreamReaders." - reader = new BufferedReader(new InputStreamReader(in)); - StringBuilder jsonString = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - // Line breaks are omitted and irrelevant - jsonString.append(line); - } - // JSONTokener parses a String in JSON "notation" into a "JSON-compatible" object. - // JSON objects are instances of JSONObject and JSONArray. You actually have to call - // nextValue() on the returned Tokener to get the corresponding JSON object. - JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()).nextValue(); - for (int i = 0; i < array.length(); i++) { - items.add(newItem(array.getJSONObject(i))); - } - } catch (JSONException e) { - throw new RuntimeException(e); - } finally { - if (reader != null) - reader.close(); - } - return items; - } - - public final void saveItems(@NonNull List items) throws IOException { - // Convert items to JSONObjects and store in a JSONArray - JSONArray array = new JSONArray(); - try { - for (T item : items) { - array.put(item.toJsonObject()); - } - } catch (JSONException e) { - throw new RuntimeException(e); - } - - OutputStreamWriter writer = null; - try { - // Create a character stream from the byte stream - writer = new OutputStreamWriter(mContext.openFileOutput(mFilename, Context.MODE_PRIVATE)); - // Write JSONArray to file - writer.write(array.toString()); - } finally { - if (writer != null) { - writer.close(); // also calls close on the byte stream - } - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/JsonSerializable.java b/app/src/main/java/com/philliphsu/clock2/model/JsonSerializable.java deleted file mode 100644 index 6ec712c..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/JsonSerializable.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.support.annotation.NonNull; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -@Deprecated -public interface JsonSerializable { - String KEY_ID = "id"; - - @NonNull JSONObject toJsonObject() throws JSONException; - long id(); -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/Repository.java b/app/src/main/java/com/philliphsu/clock2/model/Repository.java deleted file mode 100644 index a5bbda0..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/Repository.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.util.List; - -/** - * Created by Phillip Hsu on 5/31/2016. - */ -public interface Repository { - @NonNull List getItems(); - @Nullable T getItem(long id); - void addItem(@NonNull T item); - void deleteItem(@NonNull T item); - void updateItem(@NonNull T item1, @NonNull T item2); - boolean saveItems(); - void clear(); -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/TimerLoader.java b/app/src/main/java/com/philliphsu/clock2/model/TimerLoader.java deleted file mode 100644 index ba598ac..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/TimerLoader.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; - -import com.philliphsu.clock2.Timer; - -/** - * Created by Phillip Hsu on 8/3/2016. - */ -public class TimerLoader extends DataLoader { - - private long mTimerId; - - // TODO: Consider writing a super ctor that has the id param, so - // subclasses don't need to write their own. - public TimerLoader(Context context, long timerId) { - super(context); - mTimerId = timerId; - } - - @Override - public Timer loadInBackground() { - return new TimersTableManager(getContext()).queryItem(mTimerId).getItem(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java deleted file mode 100644 index 94379cc..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.philliphsu.clock2.timers; - -import android.view.ViewGroup; - -import com.philliphsu.clock2.BaseAdapter; -import com.philliphsu.clock2.OnListItemInteractionListener; -import com.philliphsu.clock2.Timer; - -import java.util.List; - -/** - * Created by Phillip Hsu on 7/26/2016. - */ -@Deprecated -public class TimerAdapter extends BaseAdapter { - - public TimerAdapter(List items, OnListItemInteractionListener listener) { - super(Timer.class, items, listener); - } - - @Override - protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { - return new TimerViewHolder(parent, listener, null); - } - - @Override - protected int compare(Timer o1, Timer o2) { - return 0; - } - - @Override - protected boolean areContentsTheSame(Timer oldItem, Timer newItem) { - return false; - } - - @Override - protected boolean areItemsTheSame(Timer item1, Timer item2) { - return false; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java b/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java deleted file mode 100644 index 23b6ba1..0000000 --- a/app/src/main/java/com/philliphsu/clock2/timers/dummy/DummyContent.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.philliphsu.clock2.timers.dummy; - -import com.philliphsu.clock2.Timer; - -import java.util.ArrayList; -import java.util.List; - -/** - * Helper class for providing sample content for user interfaces created by - * Android template wizards. - *

- * TODO: Replace all uses of this class before publishing your app. - */ -public class DummyContent { - - /** - * An array of sample (dummy) items. - */ - public static final List ITEMS = new ArrayList<>(); - - private static final int COUNT = 10; - - static { - // Add some sample items. - for (int i = 1; i <= COUNT; i++) { - addItem(createTimer(i)); - } - } - - private static void addItem(Timer item) { - ITEMS.add(item); - } - - private static Timer createTimer(int position) { - return Timer.create(1, 0, 0); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java index 87a9379..2ba8161 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java @@ -19,7 +19,7 @@ import com.philliphsu.clock2.model.AlarmsTableManager; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.app.PendingIntent.FLAG_NO_CREATE; import static android.app.PendingIntent.getActivity; -import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; +import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime; import static java.util.concurrent.TimeUnit.HOURS; /** @@ -135,8 +135,8 @@ public final class AlarmController { // passes before rescheduling the alarm. alarm.ignoreUpcomingRingTime(true); // Useful only for VH binding Intent intent = new Intent(mAppContext, PendingAlarmScheduler.class) - .putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.id()); - pi = PendingIntent.getBroadcast(mAppContext, alarm.intId(), + .putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.getId()); + pi = PendingIntent.getBroadcast(mAppContext, alarm.getIntId(), intent, PendingIntent.FLAG_ONE_SHOT); am.set(AlarmManager.RTC_WAKEUP, alarm.ringsAt(), pi); } else { @@ -177,7 +177,7 @@ public final class AlarmController { new Thread(new Runnable() { @Override public void run() { - mTableManager.updateItem(alarm.id(), alarm); + mTableManager.updateItem(alarm.getId(), alarm); } }).start(); } @@ -187,7 +187,7 @@ public final class AlarmController { Intent intent = new Intent(mAppContext, AlarmActivity.class) .putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm); int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT; - PendingIntent pi = getActivity(mAppContext, alarm.intId(), intent, flag); + PendingIntent pi = getActivity(mAppContext, alarm.getIntId(), intent, flag); // Even when we try to retrieve a previous instance that actually did exist, // null can be returned for some reason. /* @@ -208,7 +208,7 @@ public final class AlarmController { intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING); } int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT; - PendingIntent pi = PendingIntent.getBroadcast(mAppContext, alarm.intId(), intent, flag); + PendingIntent pi = PendingIntent.getBroadcast(mAppContext, alarm.getIntId(), intent, flag); // Even when we try to retrieve a previous instance that actually did exist, // null can be returned for some reason. /* diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java index d950f3a..685b06b 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java @@ -1,161 +1,21 @@ package com.philliphsu.clock2.util; -import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.preference.PreferenceManager; import android.support.annotation.StringRes; -import android.util.Log; -import android.widget.Toast; -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.PendingAlarmScheduler; import com.philliphsu.clock2.R; -import com.philliphsu.clock2.UpcomingAlarmReceiver; -import com.philliphsu.clock2.alarms.AlarmActivity; -import com.philliphsu.clock2.alarms.AlarmRingtoneService; -import com.philliphsu.clock2.model.AlarmsTableManager; - -import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; -import static android.app.PendingIntent.FLAG_NO_CREATE; -import static android.app.PendingIntent.getActivity; -import static com.philliphsu.clock2.util.DateFormatUtils.formatTime; -import static java.util.concurrent.TimeUnit.HOURS; /** * Created by Phillip Hsu on 6/3/2016. * - * Utilities for scheduling and unscheduling alarms with the {@link AlarmManager}, as well as - * managing the upcoming alarm notification. - * - * TODO: Adapt this to Timers too... - * TODO: Keep only utility methods. Not the scheduling and cancelling methods. + * Utilities for reading alarm preferences. */ public final class AlarmUtils { private static final String TAG = "AlarmUtils"; private AlarmUtils() {} - /** - * Schedules the alarm with the {@link AlarmManager}. If - * {@code alarm.}{@link Alarm#isEnabled() isEnabled()} returns false, - * this does nothing and returns immediately. - * - * @deprecated {@code showToast} is no longer working. Callers must - * handle popup confirmations on their own. - */ - // TODO: Consider moving usages to the background - public static void scheduleAlarm(Context context, Alarm alarm, boolean showToast) { - if (!alarm.isEnabled()) { - Log.i(TAG, "Skipped scheduling an alarm because it was not enabled"); - return; - } - - Log.d(TAG, "Scheduling alarm " + alarm); - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - // If there is already an alarm for this Intent scheduled (with the equality of two - // intents being defined by filterEquals(Intent)), then it will be removed and replaced - // by this one. For most of our uses, the relevant criteria for equality will be the - // action, the data, and the class (component). Although not documented, the request code - // of a PendingIntent is also considered to determine equality of two intents. - - // WAKEUP alarm types wake the CPU up, but NOT the screen. If that is what you want, you need - // to handle that yourself by using a wakelock, etc.. - // We use a WAKEUP alarm to send the upcoming alarm notification so it goes off even if the - // device is asleep. Otherwise, it will not go off until the device is turned back on. - long ringAt = alarm.isSnoozed() ? alarm.snoozingUntil() : alarm.ringsAt(); - // If snoozed, upcoming note posted immediately. - am.set(AlarmManager.RTC_WAKEUP, ringAt - HOURS.toMillis(hoursBeforeUpcoming(context)), - notifyUpcomingAlarmIntent(context, alarm, false)); - am.setExact(AlarmManager.RTC_WAKEUP, ringAt, alarmIntent(context, alarm, false)); - - // TODO: Consider removing this and letting callers handle this, because - // it could be beneficial for callers to schedule the alarm in a worker thread. - if (false && showToast) { - String message; - if (alarm.isSnoozed()) { - message = context.getString(R.string.title_snoozing_until, - formatTime(context, alarm.snoozingUntil())); - } else { - message = context.getString(R.string.alarm_set_for, - DurationUtils.toString(context, alarm.ringsIn(), false /*abbreviate?*/)); - } - // TODO: Will toasts show for any Context? e.g. IntentService can't do anything on UI thread. - Toast.makeText(context, message, Toast.LENGTH_LONG).show(); - } - } - - public static void cancelAlarm(Context c, Alarm a, boolean showToast) { - Log.d(TAG, "Cancelling alarm " + a); - AlarmManager am = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE); - - PendingIntent pi = alarmIntent(c, a, true); - if (pi != null) { - am.cancel(pi); - pi.cancel(); - } - - pi = notifyUpcomingAlarmIntent(c, a, true); - if (pi != null) { - am.cancel(pi); - pi.cancel(); - } - - removeUpcomingAlarmNotification(c, a); - - // We can't remove this and instead let callers handle Toasts - // without complication, because callers would then have to - // handle cases when the alarm is snoozed and/or has - // recurrence themselves. - // TOneverDO: Place block after making value changes to the alarm. - if (showToast && (a.ringsWithinHours(hoursBeforeUpcoming(c)) || a.isSnoozed())) { - String time = formatTime(c, a.isSnoozed() ? a.snoozingUntil() : a.ringsAt()); - String text = c.getString(R.string.upcoming_alarm_dismissed, time); - Toast.makeText(c, text, Toast.LENGTH_LONG).show(); - } - - if (a.isSnoozed()) { - a.stopSnoozing(); - } - - if (!a.hasRecurrence()) { - a.setEnabled(false); - } else { - if (a.isEnabled()) { - if (a.ringsWithinHours(hoursBeforeUpcoming(c))) { - // Still upcoming today, so wait until the normal ring time - // passes before rescheduling the alarm. - a.ignoreUpcomingRingTime(true); // Useful only for VH binding - Intent intent = new Intent(c, PendingAlarmScheduler.class) - .putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id()); - pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT); - am.set(AlarmManager.RTC_WAKEUP, a.ringsAt(), pi); - } else { - scheduleAlarm(c, a, false); - } - } - } - - save(c, a); - - // If service is not running, nothing happens - c.stopService(new Intent(c, AlarmRingtoneService.class)); - } - - public static void snoozeAlarm(Context c, Alarm a) { - a.snooze(snoozeDuration(c)); - scheduleAlarm(c, a, true); - save(c, a); - } - - public static void removeUpcomingAlarmNotification(Context c, Alarm a) { - Intent intent = new Intent(c, UpcomingAlarmReceiver.class) - .setAction(UpcomingAlarmReceiver.ACTION_CANCEL_NOTIFICATION) - .putExtra(UpcomingAlarmReceiver.EXTRA_ALARM, a); - c.sendBroadcast(intent); - } - public static int snoozeDuration(Context c) { return readPreference(c, R.string.key_snooze_duration, 10); } @@ -177,51 +37,4 @@ public final class AlarmUtils { String value = PreferenceManager.getDefaultSharedPreferences(c).getString(c.getString(key), null); return null == value ? defaultValue : Integer.parseInt(value); } - - private static PendingIntent alarmIntent(Context context, Alarm alarm, boolean retrievePrevious) { - // TODO: Use appropriate subclass instead - Intent intent = new Intent(context, AlarmActivity.class) - .putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm); - int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT; - PendingIntent pi = getActivity(context, alarm.intId(), intent, flag); - // Even when we try to retrieve a previous instance that actually did exist, - // null can be returned for some reason. -/* - if (retrievePrevious) { - checkNotNull(pi); - } -*/ - return pi; - } - - private static PendingIntent notifyUpcomingAlarmIntent(Context context, Alarm alarm, boolean retrievePrevious) { - Intent intent = new Intent(context, UpcomingAlarmReceiver.class) - .putExtra(UpcomingAlarmReceiver.EXTRA_ALARM, alarm); - if (alarm.isSnoozed()) { - // TODO: Will this affect retrieving a previous instance? Say if the previous instance - // didn't have this action set initially, but at a later time we made a new instance - // with it set. - intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING); - } - int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT; - PendingIntent pi = PendingIntent.getBroadcast(context, alarm.intId(), intent, flag); - // Even when we try to retrieve a previous instance that actually did exist, - // null can be returned for some reason. -/* - if (retrievePrevious) { - checkNotNull(pi); - } -*/ - return pi; - } - - public static void save(final Context c, final Alarm alarm) { - // TODO: Will using the Runnable like this cause a memory leak? - new Thread(new Runnable() { - @Override - public void run() { - new AlarmsTableManager(c).updateItem(alarm.id(), alarm); - } - }).start(); - } } diff --git a/app/src/main/java/com/philliphsu/clock2/util/ConversionUtils.java b/app/src/main/java/com/philliphsu/clock2/util/ConversionUtils.java deleted file mode 100644 index de72a5e..0000000 --- a/app/src/main/java/com/philliphsu/clock2/util/ConversionUtils.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.philliphsu.clock2.util; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.util.TypedValue; - -/** - * Created by Phillip Hsu on 4/21/2016. - */ -public final class ConversionUtils { - - public static float dpToPx(@NonNull final Context context, final float dp) { - return toPx(context, TypedValue.COMPLEX_UNIT_DIP, dp); - } - - public static float spToPx(@NonNull final Context context, final float sp) { - return toPx(context, TypedValue.COMPLEX_UNIT_SP, sp); - } - - private static float toPx(@NonNull final Context context, final int unit, final float val) { - // This always returns a floating point value, i.e. pixels. - return TypedValue.applyDimension(unit, val, context.getResources().getDisplayMetrics()); - } - - private ConversionUtils() {} - -} diff --git a/app/src/main/java/com/philliphsu/clock2/util/DateFormatUtils.java b/app/src/main/java/com/philliphsu/clock2/util/TimeFormatUtils.java similarity index 90% rename from app/src/main/java/com/philliphsu/clock2/util/DateFormatUtils.java rename to app/src/main/java/com/philliphsu/clock2/util/TimeFormatUtils.java index 20eb7e0..9a9f189 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/DateFormatUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/TimeFormatUtils.java @@ -10,9 +10,9 @@ import static android.text.format.DateFormat.getTimeFormat; /** * Created by Phillip Hsu on 6/3/2016. */ -public final class DateFormatUtils { +public final class TimeFormatUtils { - private DateFormatUtils() {} + private TimeFormatUtils() {} public static String formatTime(Context context, long millis) { return getTimeFormat(context).format(new Date(millis)); diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/TimeTextUtils.java b/app/src/main/java/com/philliphsu/clock2/util/TimeTextUtils.java similarity index 96% rename from app/src/main/java/com/philliphsu/clock2/editalarm/TimeTextUtils.java rename to app/src/main/java/com/philliphsu/clock2/util/TimeTextUtils.java index 2c01ddc..4b98bb0 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/TimeTextUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/TimeTextUtils.java @@ -1,4 +1,4 @@ -package com.philliphsu.clock2.editalarm; +package com.philliphsu.clock2.util; import android.text.SpannableString; import android.text.Spanned; diff --git a/app/src/main/res/layout/activity_edit_alarm.xml b/app/src/main/res/layout/activity_edit_alarm.xml deleted file mode 100644 index fd40489..0000000 --- a/app/src/main/res/layout/activity_edit_alarm.xml +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - -