Deleted deprecated and unused classes and files
This commit is contained in:
parent
f720bae5c1
commit
d5c9bcaec9
@ -2,10 +2,8 @@ package com.philliphsu.clock2;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.philliphsu.clock2.model.JsonSerializable;
|
|
||||||
import com.philliphsu.clock2.model.ObjectWithId;
|
import com.philliphsu.clock2.model.ObjectWithId;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@ -22,7 +20,7 @@ import static com.philliphsu.clock2.DaysOfWeek.SUNDAY;
|
|||||||
* Created by Phillip Hsu on 5/26/2016.
|
* Created by Phillip Hsu on 5/26/2016.
|
||||||
*/
|
*/
|
||||||
@AutoValue
|
@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;
|
private static final int MAX_MINUTES_CAN_SNOOZE = 30;
|
||||||
|
|
||||||
// =================== MUTABLE =======================
|
// =================== MUTABLE =======================
|
||||||
@ -211,26 +209,6 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa
|
|||||||
return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours);
|
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 ==============================
|
// ============================ PARCELABLE ==============================
|
||||||
// Unfortunately, we can't use the Parcelable extension for AutoValue because
|
// Unfortunately, we can't use the Parcelable extension for AutoValue because
|
||||||
// our model isn't totally immutable. Our mutable properties will be left
|
// our model isn't totally immutable. Our mutable properties will be left
|
||||||
|
|||||||
@ -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<T, VH extends BaseViewHolder<T>> extends RecyclerView.Adapter<VH> {
|
|
||||||
|
|
||||||
private final OnListItemInteractionListener<T> mListener;
|
|
||||||
private final SortedList<T> mItems;
|
|
||||||
|
|
||||||
protected BaseAdapter(Class<T> cls, List<T> items, OnListItemInteractionListener<T> listener) {
|
|
||||||
mItems = new SortedList<>(cls, new SortedListAdapterCallback<T>(this) {
|
|
||||||
@Override
|
|
||||||
public int compare(T o1, T o2) {
|
|
||||||
//return BaseAdapter.this.<T, VH>*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<T> 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<T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,14 +2,6 @@ package com.philliphsu.clock2;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 5/31/2016.
|
* 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<T> {
|
public interface OnListItemInteractionListener<T> {
|
||||||
void onListItemClick(T item, int position);
|
void onListItemClick(T item, int position);
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,7 +13,7 @@ import com.philliphsu.clock2.util.AlarmController;
|
|||||||
|
|
||||||
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
||||||
import static android.app.PendingIntent.FLAG_ONE_SHOT;
|
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.
|
// TODO: Consider registering this locally instead of in the manifest.
|
||||||
public class UpcomingAlarmReceiver extends BroadcastReceiver {
|
public class UpcomingAlarmReceiver extends BroadcastReceiver {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import com.philliphsu.clock2.R;
|
|||||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||||
import com.philliphsu.clock2.util.AlarmController;
|
import com.philliphsu.clock2.util.AlarmController;
|
||||||
import com.philliphsu.clock2.util.DateFormatUtils;
|
import com.philliphsu.clock2.util.TimeFormatUtils;
|
||||||
|
|
||||||
public class AlarmActivity extends RingtoneActivity<Alarm> {
|
public class AlarmActivity extends RingtoneActivity<Alarm> {
|
||||||
private static final String TAG = "AlarmActivity";
|
private static final String TAG = "AlarmActivity";
|
||||||
@ -117,7 +117,7 @@ public class AlarmActivity extends RingtoneActivity<Alarm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void postMissedAlarmNote() {
|
private void postMissedAlarmNote() {
|
||||||
String alarmTime = DateFormatUtils.formatTime(this,
|
String alarmTime = TimeFormatUtils.formatTime(this,
|
||||||
getRingingObject().hour(), getRingingObject().minutes());
|
getRingingObject().hour(), getRingingObject().minutes());
|
||||||
Notification note = new NotificationCompat.Builder(this)
|
Notification note = new NotificationCompat.Builder(this)
|
||||||
.setContentTitle(getString(R.string.missed_alarm))
|
.setContentTitle(getString(R.string.missed_alarm))
|
||||||
|
|||||||
@ -35,8 +35,9 @@ import java.util.Locale;
|
|||||||
* Created by Phillip Hsu on 4/30/2016.
|
* Created by Phillip Hsu on 4/30/2016.
|
||||||
*
|
*
|
||||||
* A modified version of the framework's Chronometer class that counts down toward the base time,
|
* 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
|
* uses the {@link System#currentTimeMillis()} timebase, and only has minute precision.
|
||||||
* the current timer value in the form "In H hrs and M mins". // TODO: This isn't the actual format. Verify what that is.
|
*
|
||||||
|
* TODO: Refactor by using the ChronometerDelegate class.
|
||||||
*/
|
*/
|
||||||
public class AlarmCountdown extends TextView {
|
public class AlarmCountdown extends TextView {
|
||||||
private static final String TAG = "AlarmCountdown";
|
private static final String TAG = "AlarmCountdown";
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import com.philliphsu.clock2.ringtone.RingtoneService;
|
|||||||
import com.philliphsu.clock2.util.AlarmController;
|
import com.philliphsu.clock2.util.AlarmController;
|
||||||
import com.philliphsu.clock2.util.AlarmUtils;
|
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<Alarm> {
|
public class AlarmRingtoneService extends RingtoneService<Alarm> {
|
||||||
private static final String TAG = "AlarmRingtoneService";
|
private static final String TAG = "AlarmRingtoneService";
|
||||||
|
|||||||
@ -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<Alarm, BaseAlarmViewHolder> {
|
|
||||||
|
|
||||||
public AlarmsAdapter(List<Alarm> alarms, OnListItemInteractionListener<Alarm> 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<Alarm> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -25,7 +25,7 @@ import com.philliphsu.clock2.R;
|
|||||||
import com.philliphsu.clock2.TimePickerDialogController;
|
import com.philliphsu.clock2.TimePickerDialogController;
|
||||||
import com.philliphsu.clock2.aospdatetimepicker.Utils;
|
import com.philliphsu.clock2.aospdatetimepicker.Utils;
|
||||||
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog.OnTimeSetListener;
|
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.AlarmController;
|
||||||
import com.philliphsu.clock2.util.AlarmUtils;
|
import com.philliphsu.clock2.util.AlarmUtils;
|
||||||
import com.philliphsu.clock2.util.FragmentTagUtils;
|
import com.philliphsu.clock2.util.FragmentTagUtils;
|
||||||
@ -39,7 +39,7 @@ import butterknife.OnTouch;
|
|||||||
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
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.
|
* Created by Phillip Hsu on 7/31/2016.
|
||||||
|
|||||||
@ -23,11 +23,6 @@ public class CollapsedAlarmViewHolder extends BaseAlarmViewHolder {
|
|||||||
@Bind(R.id.countdown) AlarmCountdown mCountdown;
|
@Bind(R.id.countdown) AlarmCountdown mCountdown;
|
||||||
@Bind(R.id.recurring_days) TextView mDays; // TODO: use `new DateFormatSymbols().getShortWeekdays()` to set texts
|
@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<Alarm> listener) {
|
|
||||||
super(parent, R.layout.item_collapsed_alarm, listener, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener<Alarm> listener,
|
public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener<Alarm> listener,
|
||||||
AlarmController alarmController) {
|
AlarmController alarmController) {
|
||||||
super(parent, R.layout.item_collapsed_alarm, listener, alarmController);
|
super(parent, R.layout.item_collapsed_alarm, listener, alarmController);
|
||||||
|
|||||||
@ -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.
|
|
||||||
* <p/>
|
|
||||||
* TODO: Replace all uses of this class before publishing your app.
|
|
||||||
*/
|
|
||||||
public class DummyContent {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of sample (dummy) items.
|
|
||||||
*/
|
|
||||||
public static final List<Alarm> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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<Alarm>,
|
|
||||||
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<Alarm> onCreateLoader(int id, Bundle args) {
|
|
||||||
return new AlarmLoader(this, mOldAlarmId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Alarm> loader, Alarm data) {
|
|
||||||
mOldAlarm = data;
|
|
||||||
showDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Alarm> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<Alarm> mRepository;
|
|
||||||
@NonNull private final AlarmUtilsHelper mAlarmUtilsHelper;
|
|
||||||
@NonNull private final SharedPreferencesHelper mSharedPreferencesHelper;
|
|
||||||
@Nullable private Alarm mAlarm;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public EditAlarmPresenter(@NonNull EditAlarmContract.View view,
|
|
||||||
@NonNull Repository<Alarm> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -23,9 +23,6 @@ import butterknife.OnClick;
|
|||||||
* Created by Phillip Hsu on 7/12/2016.
|
* Created by Phillip Hsu on 7/12/2016.
|
||||||
*
|
*
|
||||||
* Successor to the Numpad class that was based on TableLayout.
|
* 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
|
* TODO: Is NumpadTimePicker the only subclass? If so, why do we need this
|
||||||
* superclass? If we move the contents of this class to NumpadTimePicker,
|
* superclass? If we move the contents of this class to NumpadTimePicker,
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -9,6 +9,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.philliphsu.clock2.R;
|
import com.philliphsu.clock2.R;
|
||||||
import com.philliphsu.clock2.aospdatetimepicker.Utils;
|
import com.philliphsu.clock2.aospdatetimepicker.Utils;
|
||||||
|
import com.philliphsu.clock2.util.TimeTextUtils;
|
||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<Alarm> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<Alarm> {
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<Alarm> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<T extends JsonSerializable> implements Repository<T> {
|
|
||||||
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<T> 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<T> mItems; // TODO: Consider ArrayMap<Long, T>?
|
|
||||||
@NonNull private final JsonIoHelper<T> mIoHelper;
|
|
||||||
|
|
||||||
// TODO: Test that the callbacks work.
|
|
||||||
private DataObserver<T> 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<T2> {
|
|
||||||
void onItemAdded(T2 item);
|
|
||||||
void onItemDeleted(T2 item);
|
|
||||||
void onItemUpdated(T2 oldItem, T2 newItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*package-private*/ BaseRepository(@NonNull Context context,
|
|
||||||
@NonNull JsonIoHelper<T> 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<T> 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<T> observer) {
|
|
||||||
mDataObserver = observer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Do we need to call this?
|
|
||||||
public final void unregisterDataObserver() {
|
|
||||||
mDataObserver = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<T> loadItems() {
|
|
||||||
try {
|
|
||||||
return mIoHelper.loadItems();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Error loading items from file: " + e);
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<D> extends AsyncTaskLoader<D> {
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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<T extends JsonSerializable> {
|
|
||||||
@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<T> loadItems() throws IOException {
|
|
||||||
ArrayList<T> 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<T> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
@ -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<T> {
|
|
||||||
@NonNull List<T> 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();
|
|
||||||
}
|
|
||||||
@ -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<Timer> {
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<Timer, TimerViewHolder> {
|
|
||||||
|
|
||||||
public TimerAdapter(List<Timer> items, OnListItemInteractionListener<Timer> listener) {
|
|
||||||
super(Timer.class, items, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener<Timer> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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.
|
|
||||||
* <p/>
|
|
||||||
* TODO: Replace all uses of this class before publishing your app.
|
|
||||||
*/
|
|
||||||
public class DummyContent {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array of sample (dummy) items.
|
|
||||||
*/
|
|
||||||
public static final List<Timer> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -19,7 +19,7 @@ import com.philliphsu.clock2.model.AlarmsTableManager;
|
|||||||
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
||||||
import static android.app.PendingIntent.FLAG_NO_CREATE;
|
import static android.app.PendingIntent.FLAG_NO_CREATE;
|
||||||
import static android.app.PendingIntent.getActivity;
|
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;
|
import static java.util.concurrent.TimeUnit.HOURS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,8 +135,8 @@ public final class AlarmController {
|
|||||||
// passes before rescheduling the alarm.
|
// passes before rescheduling the alarm.
|
||||||
alarm.ignoreUpcomingRingTime(true); // Useful only for VH binding
|
alarm.ignoreUpcomingRingTime(true); // Useful only for VH binding
|
||||||
Intent intent = new Intent(mAppContext, PendingAlarmScheduler.class)
|
Intent intent = new Intent(mAppContext, PendingAlarmScheduler.class)
|
||||||
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.id());
|
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.getId());
|
||||||
pi = PendingIntent.getBroadcast(mAppContext, alarm.intId(),
|
pi = PendingIntent.getBroadcast(mAppContext, alarm.getIntId(),
|
||||||
intent, PendingIntent.FLAG_ONE_SHOT);
|
intent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
am.set(AlarmManager.RTC_WAKEUP, alarm.ringsAt(), pi);
|
am.set(AlarmManager.RTC_WAKEUP, alarm.ringsAt(), pi);
|
||||||
} else {
|
} else {
|
||||||
@ -177,7 +177,7 @@ public final class AlarmController {
|
|||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mTableManager.updateItem(alarm.id(), alarm);
|
mTableManager.updateItem(alarm.getId(), alarm);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ public final class AlarmController {
|
|||||||
Intent intent = new Intent(mAppContext, AlarmActivity.class)
|
Intent intent = new Intent(mAppContext, AlarmActivity.class)
|
||||||
.putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm);
|
.putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm);
|
||||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
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,
|
// Even when we try to retrieve a previous instance that actually did exist,
|
||||||
// null can be returned for some reason.
|
// null can be returned for some reason.
|
||||||
/*
|
/*
|
||||||
@ -208,7 +208,7 @@ public final class AlarmController {
|
|||||||
intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING);
|
intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING);
|
||||||
}
|
}
|
||||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
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,
|
// Even when we try to retrieve a previous instance that actually did exist,
|
||||||
// null can be returned for some reason.
|
// null can be returned for some reason.
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -1,161 +1,21 @@
|
|||||||
package com.philliphsu.clock2.util;
|
package com.philliphsu.clock2.util;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.StringRes;
|
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.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.
|
* Created by Phillip Hsu on 6/3/2016.
|
||||||
*
|
*
|
||||||
* Utilities for scheduling and unscheduling alarms with the {@link AlarmManager}, as well as
|
* Utilities for reading alarm preferences.
|
||||||
* managing the upcoming alarm notification.
|
|
||||||
*
|
|
||||||
* TODO: Adapt this to Timers too...
|
|
||||||
* TODO: Keep only utility methods. Not the scheduling and cancelling methods.
|
|
||||||
*/
|
*/
|
||||||
public final class AlarmUtils {
|
public final class AlarmUtils {
|
||||||
private static final String TAG = "AlarmUtils";
|
private static final String TAG = "AlarmUtils";
|
||||||
|
|
||||||
private 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) {
|
public static int snoozeDuration(Context c) {
|
||||||
return readPreference(c, R.string.key_snooze_duration, 10);
|
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);
|
String value = PreferenceManager.getDefaultSharedPreferences(c).getString(c.getString(key), null);
|
||||||
return null == value ? defaultValue : Integer.parseInt(value);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() {}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -10,9 +10,9 @@ import static android.text.format.DateFormat.getTimeFormat;
|
|||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 6/3/2016.
|
* 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) {
|
public static String formatTime(Context context, long millis) {
|
||||||
return getTimeFormat(context).format(new Date(millis));
|
return getTimeFormat(context).format(new Date(millis));
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.philliphsu.clock2.editalarm;
|
package com.philliphsu.clock2.util;
|
||||||
|
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
@ -1,225 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:context=".editalarm.EditAlarmActivity"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:id="@+id/main_content">
|
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?actionBarSize"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
app:popupTheme="@style/AppTheme.PopupOverlay"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:elevation="4dp"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/footer_buttons"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?android:buttonBarStyle"
|
|
||||||
android:elevation="4dp"
|
|
||||||
android:layoutDirection="rtl"
|
|
||||||
android:layout_alignParentBottom="true">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/save"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/save"
|
|
||||||
android:textColor="@color/colorAccent"
|
|
||||||
style="?borderlessButtonStyle"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/delete"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/delete"
|
|
||||||
android:textColor="@color/colorAccent"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Button"
|
|
||||||
style="?borderlessButtonStyle"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_below="@id/toolbar"
|
|
||||||
android:layout_above="@id/footer_buttons">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
|
||||||
|
|
||||||
<android.support.v7.widget.SwitchCompat
|
|
||||||
android:id="@+id/on_off"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"/>
|
|
||||||
|
|
||||||
<!-- TODO: Find out how to get a touch ripple; foreground attr didn't work -->
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/input_time"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:foreground="?selectableItemBackground"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:hint="@string/default_alarm_time_12h"
|
|
||||||
android:textSize="56sp"
|
|
||||||
android:layout_toStartOf="@id/on_off"
|
|
||||||
android:inputType="time"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day0"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day1"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day2"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day3"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day4"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day5"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day6"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/label"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:hint="Add label"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/ringtone"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:text="Ringtone"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_toStartOf="@+id/vibrate"/>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@id/vibrate"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="Vibrate"
|
|
||||||
android:layout_alignParentEnd="true"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<com.philliphsu.clock2.editalarm.AlarmNumpad
|
|
||||||
android:id="@+id/numpad"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
|
||||||
android:elevation="4dp"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<!-- For elevation to apply, we set a non-transparent background -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/footer_buttons"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:background="?android:colorBackground"
|
|
||||||
android:elevation="4dp"
|
|
||||||
android:layout_alignParentBottom="true">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/delete"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/delete"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/save"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/save"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<android.support.design.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_above="@id/footer_buttons"
|
|
||||||
android:layout_alignParentTop="true">
|
|
||||||
|
|
||||||
<!-- We don't intend to use any scroll flags, but this is needed
|
|
||||||
so we can properly position our scrolling content below the appbar -->
|
|
||||||
<android.support.design.widget.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/AppTheme.AppBarOverlay"
|
|
||||||
android:elevation="4dp">
|
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?actionBarSize"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
|
||||||
|
|
||||||
<android.support.v4.widget.NestedScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
|
||||||
|
|
||||||
<android.support.v7.widget.SwitchCompat
|
|
||||||
android:id="@+id/on_off"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/input_time"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:hint="@string/default_alarm_time_12h"
|
|
||||||
android:textSize="56sp"
|
|
||||||
android:layout_toStartOf="@id/on_off"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:background="@color/colorPrimary"
|
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day0"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day1"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day2"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day3"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day4"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day5"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToggleButton
|
|
||||||
android:id="@+id/day6"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textColor="@color/toggle_alarm_days"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/label"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:hint="Add label"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/ringtone"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:text="Ringtone"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_toStartOf="@+id/vibrate"/>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@id/vibrate"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="Vibrate"
|
|
||||||
android:layout_alignParentEnd="true"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</android.support.v4.widget.NestedScrollView>
|
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<android.support.v4.widget.NestedScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
tools:context="com.philliphsu.clock2.editalarm.EditAlarmActivity"
|
|
||||||
tools:showIn="@layout/activity_edit_alarm">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/text_margin"
|
|
||||||
android:text="@string/large_text"/>
|
|
||||||
|
|
||||||
</android.support.v4.widget.NestedScrollView>
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:context="com.philliphsu.clock2.editalarm.EditAlarmActivity">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_dismiss_now"
|
|
||||||
android:title="@string/dismiss_now"
|
|
||||||
android:icon="@android:drawable/ic_delete"
|
|
||||||
app:showAsAction="ifRoom"
|
|
||||||
android:visible="false"/>
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_done_snoozing"
|
|
||||||
android:title="@string/done_snoozing"
|
|
||||||
android:icon="@android:drawable/ic_delete"
|
|
||||||
app:showAsAction="ifRoom"
|
|
||||||
android:visible="false"/>
|
|
||||||
</menu>
|
|
||||||
Loading…
Reference in New Issue
Block a user