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.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.philliphsu.clock2.model.JsonSerializable;
|
||||
import com.philliphsu.clock2.model.ObjectWithId;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@ -22,7 +20,7 @@ import static com.philliphsu.clock2.DaysOfWeek.SUNDAY;
|
||||
* Created by Phillip Hsu on 5/26/2016.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class Alarm extends ObjectWithId implements JsonSerializable, Parcelable {
|
||||
public abstract class Alarm extends ObjectWithId implements Parcelable {
|
||||
private static final int MAX_MINUTES_CAN_SNOOZE = 30;
|
||||
|
||||
// =================== MUTABLE =======================
|
||||
@ -211,26 +209,6 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa
|
||||
return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours);
|
||||
}
|
||||
|
||||
// TODO: Rename to getIntId() so usages refer to ObjectWithId#getIntId(), then delete this method.
|
||||
public int intId() {
|
||||
return getIntId();
|
||||
}
|
||||
|
||||
// TODO: Remove method signature from JsonSerializable interface.
|
||||
// TODO: Remove final modifier.
|
||||
// TODO: Rename to getId() so usages refer to ObjectWithId#getId(), then delete this method.
|
||||
@Override
|
||||
public final long id() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
@NonNull
|
||||
public JSONObject toJsonObject() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// ============================ PARCELABLE ==============================
|
||||
// Unfortunately, we can't use the Parcelable extension for AutoValue because
|
||||
// our model isn't totally immutable. Our mutable properties will be left
|
||||
|
||||
@ -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.
|
||||
* 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> {
|
||||
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_ONE_SHOT;
|
||||
import static com.philliphsu.clock2.util.DateFormatUtils.formatTime;
|
||||
import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime;
|
||||
|
||||
// TODO: Consider registering this locally instead of in the manifest.
|
||||
public class UpcomingAlarmReceiver extends BroadcastReceiver {
|
||||
|
||||
@ -12,7 +12,7 @@ import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
import com.philliphsu.clock2.util.DateFormatUtils;
|
||||
import com.philliphsu.clock2.util.TimeFormatUtils;
|
||||
|
||||
public class AlarmActivity extends RingtoneActivity<Alarm> {
|
||||
private static final String TAG = "AlarmActivity";
|
||||
@ -117,7 +117,7 @@ public class AlarmActivity extends RingtoneActivity<Alarm> {
|
||||
}
|
||||
|
||||
private void postMissedAlarmNote() {
|
||||
String alarmTime = DateFormatUtils.formatTime(this,
|
||||
String alarmTime = TimeFormatUtils.formatTime(this,
|
||||
getRingingObject().hour(), getRingingObject().minutes());
|
||||
Notification note = new NotificationCompat.Builder(this)
|
||||
.setContentTitle(getString(R.string.missed_alarm))
|
||||
|
||||
@ -35,8 +35,9 @@ import java.util.Locale;
|
||||
* Created by Phillip Hsu on 4/30/2016.
|
||||
*
|
||||
* A modified version of the framework's Chronometer class that counts down toward the base time,
|
||||
* uses the {@link System#currentTimeMillis()} timebase, and only has minute precision. It displays
|
||||
* the current timer value in the form "In H hrs and M mins". // TODO: This isn't the actual format. Verify what that is.
|
||||
* uses the {@link System#currentTimeMillis()} timebase, and only has minute precision.
|
||||
*
|
||||
* TODO: Refactor by using the ChronometerDelegate class.
|
||||
*/
|
||||
public class AlarmCountdown extends TextView {
|
||||
private static final String TAG = "AlarmCountdown";
|
||||
|
||||
@ -12,7 +12,7 @@ import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
import com.philliphsu.clock2.util.AlarmUtils;
|
||||
|
||||
import static com.philliphsu.clock2.util.DateFormatUtils.formatTime;
|
||||
import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime;
|
||||
|
||||
public class AlarmRingtoneService extends RingtoneService<Alarm> {
|
||||
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.aospdatetimepicker.Utils;
|
||||
import com.philliphsu.clock2.editalarm.BaseTimePickerDialog.OnTimeSetListener;
|
||||
import com.philliphsu.clock2.editalarm.TimeTextUtils;
|
||||
import com.philliphsu.clock2.util.TimeTextUtils;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
import com.philliphsu.clock2.util.AlarmUtils;
|
||||
import com.philliphsu.clock2.util.FragmentTagUtils;
|
||||
@ -39,7 +39,7 @@ import butterknife.OnTouch;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static com.philliphsu.clock2.util.DateFormatUtils.formatTime;
|
||||
import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 7/31/2016.
|
||||
|
||||
@ -23,11 +23,6 @@ public class CollapsedAlarmViewHolder extends BaseAlarmViewHolder {
|
||||
@Bind(R.id.countdown) AlarmCountdown mCountdown;
|
||||
@Bind(R.id.recurring_days) TextView mDays; // TODO: use `new DateFormatSymbols().getShortWeekdays()` to set texts
|
||||
|
||||
@Deprecated // TODO: Delete this, the only usage is from AlarmsAdapter (SortedList), which is not used anymore.
|
||||
public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener<Alarm> listener) {
|
||||
super(parent, R.layout.item_collapsed_alarm, listener, null);
|
||||
}
|
||||
|
||||
public CollapsedAlarmViewHolder(ViewGroup parent, OnListItemInteractionListener<Alarm> listener,
|
||||
AlarmController 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.
|
||||
*
|
||||
* Successor to the Numpad class that was based on TableLayout.
|
||||
* Unlike Numpad, this class only manages the logic for number button clicks
|
||||
* and not the backspace button. However, we do provide an API for removing
|
||||
* digits from the input.
|
||||
*
|
||||
* TODO: Is NumpadTimePicker the only subclass? If so, why do we need this
|
||||
* superclass? If we move the contents of this class to NumpadTimePicker,
|
||||
|
||||
@ -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.aospdatetimepicker.Utils;
|
||||
import com.philliphsu.clock2.util.TimeTextUtils;
|
||||
|
||||
import butterknife.Bind;
|
||||
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_NO_CREATE;
|
||||
import static android.app.PendingIntent.getActivity;
|
||||
import static com.philliphsu.clock2.util.DateFormatUtils.formatTime;
|
||||
import static com.philliphsu.clock2.util.TimeFormatUtils.formatTime;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
/**
|
||||
@ -135,8 +135,8 @@ public final class AlarmController {
|
||||
// passes before rescheduling the alarm.
|
||||
alarm.ignoreUpcomingRingTime(true); // Useful only for VH binding
|
||||
Intent intent = new Intent(mAppContext, PendingAlarmScheduler.class)
|
||||
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.id());
|
||||
pi = PendingIntent.getBroadcast(mAppContext, alarm.intId(),
|
||||
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, alarm.getId());
|
||||
pi = PendingIntent.getBroadcast(mAppContext, alarm.getIntId(),
|
||||
intent, PendingIntent.FLAG_ONE_SHOT);
|
||||
am.set(AlarmManager.RTC_WAKEUP, alarm.ringsAt(), pi);
|
||||
} else {
|
||||
@ -177,7 +177,7 @@ public final class AlarmController {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mTableManager.updateItem(alarm.id(), alarm);
|
||||
mTableManager.updateItem(alarm.getId(), alarm);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
@ -187,7 +187,7 @@ public final class AlarmController {
|
||||
Intent intent = new Intent(mAppContext, AlarmActivity.class)
|
||||
.putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm);
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = getActivity(mAppContext, alarm.intId(), intent, flag);
|
||||
PendingIntent pi = getActivity(mAppContext, alarm.getIntId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
// null can be returned for some reason.
|
||||
/*
|
||||
@ -208,7 +208,7 @@ public final class AlarmController {
|
||||
intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING);
|
||||
}
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = PendingIntent.getBroadcast(mAppContext, alarm.intId(), intent, flag);
|
||||
PendingIntent pi = PendingIntent.getBroadcast(mAppContext, alarm.getIntId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
// null can be returned for some reason.
|
||||
/*
|
||||
|
||||
@ -1,161 +1,21 @@
|
||||
package com.philliphsu.clock2.util;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.PendingAlarmScheduler;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
||||
import com.philliphsu.clock2.alarms.AlarmActivity;
|
||||
import com.philliphsu.clock2.alarms.AlarmRingtoneService;
|
||||
import com.philliphsu.clock2.model.AlarmsTableManager;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
||||
import static android.app.PendingIntent.FLAG_NO_CREATE;
|
||||
import static android.app.PendingIntent.getActivity;
|
||||
import static com.philliphsu.clock2.util.DateFormatUtils.formatTime;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 6/3/2016.
|
||||
*
|
||||
* Utilities for scheduling and unscheduling alarms with the {@link AlarmManager}, as well as
|
||||
* managing the upcoming alarm notification.
|
||||
*
|
||||
* TODO: Adapt this to Timers too...
|
||||
* TODO: Keep only utility methods. Not the scheduling and cancelling methods.
|
||||
* Utilities for reading alarm preferences.
|
||||
*/
|
||||
public final class AlarmUtils {
|
||||
private static final String TAG = "AlarmUtils";
|
||||
|
||||
private AlarmUtils() {}
|
||||
|
||||
/**
|
||||
* Schedules the alarm with the {@link AlarmManager}. If
|
||||
* {@code alarm.}{@link Alarm#isEnabled() isEnabled()} returns false,
|
||||
* this does nothing and returns immediately.
|
||||
*
|
||||
* @deprecated {@code showToast} is no longer working. Callers must
|
||||
* handle popup confirmations on their own.
|
||||
*/
|
||||
// TODO: Consider moving usages to the background
|
||||
public static void scheduleAlarm(Context context, Alarm alarm, boolean showToast) {
|
||||
if (!alarm.isEnabled()) {
|
||||
Log.i(TAG, "Skipped scheduling an alarm because it was not enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Scheduling alarm " + alarm);
|
||||
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
// If there is already an alarm for this Intent scheduled (with the equality of two
|
||||
// intents being defined by filterEquals(Intent)), then it will be removed and replaced
|
||||
// by this one. For most of our uses, the relevant criteria for equality will be the
|
||||
// action, the data, and the class (component). Although not documented, the request code
|
||||
// of a PendingIntent is also considered to determine equality of two intents.
|
||||
|
||||
// WAKEUP alarm types wake the CPU up, but NOT the screen. If that is what you want, you need
|
||||
// to handle that yourself by using a wakelock, etc..
|
||||
// We use a WAKEUP alarm to send the upcoming alarm notification so it goes off even if the
|
||||
// device is asleep. Otherwise, it will not go off until the device is turned back on.
|
||||
long ringAt = alarm.isSnoozed() ? alarm.snoozingUntil() : alarm.ringsAt();
|
||||
// If snoozed, upcoming note posted immediately.
|
||||
am.set(AlarmManager.RTC_WAKEUP, ringAt - HOURS.toMillis(hoursBeforeUpcoming(context)),
|
||||
notifyUpcomingAlarmIntent(context, alarm, false));
|
||||
am.setExact(AlarmManager.RTC_WAKEUP, ringAt, alarmIntent(context, alarm, false));
|
||||
|
||||
// TODO: Consider removing this and letting callers handle this, because
|
||||
// it could be beneficial for callers to schedule the alarm in a worker thread.
|
||||
if (false && showToast) {
|
||||
String message;
|
||||
if (alarm.isSnoozed()) {
|
||||
message = context.getString(R.string.title_snoozing_until,
|
||||
formatTime(context, alarm.snoozingUntil()));
|
||||
} else {
|
||||
message = context.getString(R.string.alarm_set_for,
|
||||
DurationUtils.toString(context, alarm.ringsIn(), false /*abbreviate?*/));
|
||||
}
|
||||
// TODO: Will toasts show for any Context? e.g. IntentService can't do anything on UI thread.
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelAlarm(Context c, Alarm a, boolean showToast) {
|
||||
Log.d(TAG, "Cancelling alarm " + a);
|
||||
AlarmManager am = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
PendingIntent pi = alarmIntent(c, a, true);
|
||||
if (pi != null) {
|
||||
am.cancel(pi);
|
||||
pi.cancel();
|
||||
}
|
||||
|
||||
pi = notifyUpcomingAlarmIntent(c, a, true);
|
||||
if (pi != null) {
|
||||
am.cancel(pi);
|
||||
pi.cancel();
|
||||
}
|
||||
|
||||
removeUpcomingAlarmNotification(c, a);
|
||||
|
||||
// We can't remove this and instead let callers handle Toasts
|
||||
// without complication, because callers would then have to
|
||||
// handle cases when the alarm is snoozed and/or has
|
||||
// recurrence themselves.
|
||||
// TOneverDO: Place block after making value changes to the alarm.
|
||||
if (showToast && (a.ringsWithinHours(hoursBeforeUpcoming(c)) || a.isSnoozed())) {
|
||||
String time = formatTime(c, a.isSnoozed() ? a.snoozingUntil() : a.ringsAt());
|
||||
String text = c.getString(R.string.upcoming_alarm_dismissed, time);
|
||||
Toast.makeText(c, text, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
if (a.isSnoozed()) {
|
||||
a.stopSnoozing();
|
||||
}
|
||||
|
||||
if (!a.hasRecurrence()) {
|
||||
a.setEnabled(false);
|
||||
} else {
|
||||
if (a.isEnabled()) {
|
||||
if (a.ringsWithinHours(hoursBeforeUpcoming(c))) {
|
||||
// Still upcoming today, so wait until the normal ring time
|
||||
// passes before rescheduling the alarm.
|
||||
a.ignoreUpcomingRingTime(true); // Useful only for VH binding
|
||||
Intent intent = new Intent(c, PendingAlarmScheduler.class)
|
||||
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id());
|
||||
pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT);
|
||||
am.set(AlarmManager.RTC_WAKEUP, a.ringsAt(), pi);
|
||||
} else {
|
||||
scheduleAlarm(c, a, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save(c, a);
|
||||
|
||||
// If service is not running, nothing happens
|
||||
c.stopService(new Intent(c, AlarmRingtoneService.class));
|
||||
}
|
||||
|
||||
public static void snoozeAlarm(Context c, Alarm a) {
|
||||
a.snooze(snoozeDuration(c));
|
||||
scheduleAlarm(c, a, true);
|
||||
save(c, a);
|
||||
}
|
||||
|
||||
public static void removeUpcomingAlarmNotification(Context c, Alarm a) {
|
||||
Intent intent = new Intent(c, UpcomingAlarmReceiver.class)
|
||||
.setAction(UpcomingAlarmReceiver.ACTION_CANCEL_NOTIFICATION)
|
||||
.putExtra(UpcomingAlarmReceiver.EXTRA_ALARM, a);
|
||||
c.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public static int snoozeDuration(Context c) {
|
||||
return readPreference(c, R.string.key_snooze_duration, 10);
|
||||
}
|
||||
@ -177,51 +37,4 @@ public final class AlarmUtils {
|
||||
String value = PreferenceManager.getDefaultSharedPreferences(c).getString(c.getString(key), null);
|
||||
return null == value ? defaultValue : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
private static PendingIntent alarmIntent(Context context, Alarm alarm, boolean retrievePrevious) {
|
||||
// TODO: Use appropriate subclass instead
|
||||
Intent intent = new Intent(context, AlarmActivity.class)
|
||||
.putExtra(AlarmActivity.EXTRA_RINGING_OBJECT, alarm);
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = getActivity(context, alarm.intId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
// null can be returned for some reason.
|
||||
/*
|
||||
if (retrievePrevious) {
|
||||
checkNotNull(pi);
|
||||
}
|
||||
*/
|
||||
return pi;
|
||||
}
|
||||
|
||||
private static PendingIntent notifyUpcomingAlarmIntent(Context context, Alarm alarm, boolean retrievePrevious) {
|
||||
Intent intent = new Intent(context, UpcomingAlarmReceiver.class)
|
||||
.putExtra(UpcomingAlarmReceiver.EXTRA_ALARM, alarm);
|
||||
if (alarm.isSnoozed()) {
|
||||
// TODO: Will this affect retrieving a previous instance? Say if the previous instance
|
||||
// didn't have this action set initially, but at a later time we made a new instance
|
||||
// with it set.
|
||||
intent.setAction(UpcomingAlarmReceiver.ACTION_SHOW_SNOOZING);
|
||||
}
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = PendingIntent.getBroadcast(context, alarm.intId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
// null can be returned for some reason.
|
||||
/*
|
||||
if (retrievePrevious) {
|
||||
checkNotNull(pi);
|
||||
}
|
||||
*/
|
||||
return pi;
|
||||
}
|
||||
|
||||
public static void save(final Context c, final Alarm alarm) {
|
||||
// TODO: Will using the Runnable like this cause a memory leak?
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new AlarmsTableManager(c).updateItem(alarm.id(), alarm);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
public final class DateFormatUtils {
|
||||
public final class TimeFormatUtils {
|
||||
|
||||
private DateFormatUtils() {}
|
||||
private TimeFormatUtils() {}
|
||||
|
||||
public static String formatTime(Context context, long millis) {
|
||||
return getTimeFormat(context).format(new Date(millis));
|
||||
@ -1,4 +1,4 @@
|
||||
package com.philliphsu.clock2.editalarm;
|
||||
package com.philliphsu.clock2.util;
|
||||
|
||||
import android.text.SpannableString;
|
||||
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