diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4338caa..6588976 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,7 +41,8 @@
android:name=".editalarm.EditAlarmActivity"
android:label="@string/title_activity_edit_alarm"
android:parentActivityName=".MainActivity"
- android:theme="@style/AppTheme.NoActionBar">
+ android:theme="@style/AppTheme.NoActionBar"
+ android:windowSoftInputMode="adjustNothing">
diff --git a/app/src/main/java/com/philliphsu/clock2/Numpad.java b/app/src/main/java/com/philliphsu/clock2/Numpad.java
new file mode 100644
index 0000000..76cc320
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/Numpad.java
@@ -0,0 +1,254 @@
+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; // TODO: useless?
+ 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(); // TODO: useless?
+ 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 newRow() {
+ TableRow newRow = new TableRow(getContext());
+ newRow.setLayoutParams(new TableRow.LayoutParams());
+ addView(newRow);
+ for (int i = 0; i < NUM_COLUMNS; i++) {
+ Space s = new Space(getContext());
+ setButtonLayoutParams(s);
+ newRow.addView(s);
+ }
+ }
+
+ protected final View buildButton(@LayoutRes int buttonRes, int r, int c) {
+ View button = View.inflate(getContext(), buttonRes, null);
+ setButtonLayoutParams(button);
+ replace(r, c, button);
+ return button;
+ }
+
+ @OnClick({ R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
+ R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine })
+ protected void onClick(Button button) {
+ onClick(button, mCount);
+ }
+
+ public void onClick(Button button, int at) {
+ if (mCount < mInput.length) {
+ checkIndexWithinBounds(at);
+ mInput[at] = Integer.parseInt(button.getText().toString());
+ mCount++;
+ }
+ }
+
+ /** Performs an artificial click on the Button with the specified digit. */
+ protected final void performClick(int digit) {
+ if (digit < 0 || digit >= mButtons.length)
+ throw new ArrayIndexOutOfBoundsException("No Button with digit " + digit);
+ if (!mButtons[digit].isEnabled())
+ throw new IllegalArgumentException("Button " + digit + " is disabled. " +
+ "Did you call AlarmNumpad.setInput(String) with an invalid time?");
+ onClick(mButtons[digit]);
+ }
+
+ /** Performs an artificial click on the Button with the specified digit. */
+ protected final void performClick(char charDigit) {
+ performClick(asDigit(charDigit));
+ }
+
+ protected void setInput(int... digits) {
+ if (digits.length != mInput.length)
+ throw new IllegalArgumentException("Input arrays not the same length");
+ for (int i = 0; i < digits.length; i++) {
+ if (digits[i] < 0 || digits[i] > 9)
+ throw new IllegalArgumentException("Element in input out of range");
+ if (!mButtons[i].isEnabled())
+ throw new IllegalStateException("Button with digit " + digits[i] + " is disabled");
+ mInput[i] = digits[i];
+ }
+ }
+
+ protected void backspace() {
+ if (mCount - 1 >= 0) {
+ mInput[--mCount] = UNMODIFIED;
+ }
+ }
+
+ // public to allow hosts of this numpad to modify its contents
+ public void backspace(int at) {
+ if (at < 0 || at > mInput.length /* at == mInput.length is valid */)
+ throw new IndexOutOfBoundsException("Cannot backspace on index " + at);
+ if (at - 1 >= 0) {
+ mInput[--at] = UNMODIFIED;
+ mCount--;
+ }
+ }
+
+ protected boolean longBackspace() {
+ Arrays.fill(mInput, UNMODIFIED);
+ mCount = 0;
+ return true;
+ }
+
+ protected void setBackspaceEnabled(boolean enabled) {
+ mBackspace.setEnabled(enabled);
+ }
+
+ protected final void notifyOnNumberInputListener(String number) {
+ checkKeyListenerSet();
+ mKeyListener.onNumberInput(number);
+ }
+
+ protected final void notifyOnBackspaceListener(String newStr) {
+ checkKeyListenerSet();
+ mKeyListener.onBackspace(newStr);
+ }
+
+ protected final void notifyOnLongBackspaceListener() {
+ checkKeyListenerSet();
+ mKeyListener.onLongBackspace();
+ }
+
+ private void setButtonLayoutParams(View target) {
+ target.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.MATCH_PARENT, 1));
+ }
+
+ private void replace(int r, int c, View newView) {
+ checkLocation(r, c);
+ TableRow row = (TableRow) getChildAt(r);
+ row.removeViewAt(c);
+ row.addView(newView, c);
+ }
+
+ // Checks if the specified location in the View hierarchy exists.
+ private void checkLocation(int r, int c) {
+ if (r < 0 || r >= getChildCount())
+ throw new IndexOutOfBoundsException("No TableRow at row " + r);
+ if (c < 0 || c >= NUM_COLUMNS)
+ throw new IndexOutOfBoundsException("No column " + c + " at row " + r);
+ }
+
+ private void checkIndexWithinBounds(int i) {
+ if (i < 0 || i >= mInput.length) {
+ throw new ArrayIndexOutOfBoundsException("Index " + i + "out of bounds");
+ }
+ }
+
+ private void checkKeyListenerSet() {
+ if (null == mKeyListener)
+ throw new NullPointerException("Numpad Key listener not set");
+ }
+
+ private int asDigit(char charDigit) {
+ if (!Character.isDigit(charDigit))
+ throw new IllegalArgumentException("Character is not a digit");
+ return Character.digit(charDigit, RADIX_10);
+ }
+
+ private void init() {
+ View.inflate(getContext(), R.layout.content_numpad, this);
+ ButterKnife.bind(this);
+ if (capacity() < 0)
+ throw new IllegalArgumentException("Negative capacity");
+ mInput = new int[capacity()];
+ Arrays.fill(mInput, UNMODIFIED);
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java
new file mode 100644
index 0000000..688bc67
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmEditText.java
@@ -0,0 +1,43 @@
+package com.philliphsu.clock2.editalarm;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+
+/**
+ * Created by Phillip Hsu on 6/2/2016.
+ */
+public class AlarmEditText extends android.support.v7.widget.AppCompatEditText {
+
+ private OnBackPressListener mOnBackPressListener;
+
+ public AlarmEditText(Context context) {
+ super(context);
+ }
+
+ public AlarmEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlarmEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ && event.getAction() == KeyEvent.ACTION_UP
+ && mOnBackPressListener != null) {
+ mOnBackPressListener.onBackPress();
+ }
+ return super.dispatchKeyEvent(event); // See http://stackoverflow.com/a/5993196/5055032
+ }
+
+ public void setOnBackPressListener(OnBackPressListener onBackPressListener) {
+ mOnBackPressListener = onBackPressListener;
+ }
+
+ public interface OnBackPressListener {
+ void onBackPress();
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java
new file mode 100644
index 0000000..ff26aee
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/editalarm/AlarmNumpad.java
@@ -0,0 +1,463 @@
+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());
+ notifyOnLongBackspaceListener();
+ updateNumpadStates();
+ mAmPmState = UNSPECIFIED;
+ return consumed;
+ }
+
+ public void setTime(int hours, int minutes) {
+ if (hours < 0 || hours > 23)
+ throw new IllegalArgumentException("Illegal hours: " + hours);
+ if (minutes < 0 || minutes > 59)
+ throw new IllegalArgumentException("Illegal minutes: " + minutes);
+
+ // Internal representation of the time has been checked for legality.
+ // Now we need to format it depending on the user's clock system.
+ // If 12-hour clock, can't set mAmPmState yet or else this interferes
+ // with the button state update mechanism. Instead, cache the state
+ // the hour would resolve to in a local variable and set it after
+ // all digits are inputted.
+ int amPmState;
+ if (!is24HourFormat()) {
+ // Convert 24-hour times into 12-hour compatible times.
+ if (hours == 0) {
+ hours = 12;
+ amPmState = AM;
+ } else if (hours == 12) {
+ amPmState = PM;
+ } else if (hours > 12) {
+ hours -= 12;
+ amPmState = PM;
+ } else {
+ amPmState = AM;
+ }
+ } else {
+ amPmState = HRS_24;
+ }
+
+ // Only if on 24-hour clock, zero-pad single digit hours.
+ // Zero cannot be the first digit of any time in the 12-hour clock.
+ String strHrs = is24HourFormat()
+ ? String.format("%02d", hours)
+ : String.valueOf(hours);
+ String strMins = String.format("%02d", minutes); // Zero-pad single digit minutes
+
+ // TODO: Do this off the main thread
+ for (int i = 0; i < strHrs.length(); i++)
+ performClick(strHrs.charAt(i));
+ for (int i = 0; i < strMins.length(); i++)
+ performClick(strMins.charAt(i));
+
+ mAmPmState = amPmState;
+ if (mAmPmState != HRS_24) {
+ onAltButtonClick(mAmPmState == AM ? leftAlt : rightAlt);
+ }
+ }
+
+ public String getTime() {
+ return mFormattedInput.toString();
+ }
+
+ private void updateAltButtonStates() {
+ if (count() == 0) {
+ // No input, no access!
+ leftAlt.setEnabled(false);
+ rightAlt.setEnabled(false);
+ } else if (count() == 1) {
+ // Any of 0-9 inputted, always have access in either clock.
+ leftAlt.setEnabled(true);
+ rightAlt.setEnabled(true);
+ } else if (count() == 2) {
+ // Any 2 digits that make a valid hour for either clock are eligible for access
+ int time = getInput();
+ boolean validTwoDigitHour = is24HourFormat() ? time <= 23 : time >= 10 && time <= 12;
+ leftAlt.setEnabled(validTwoDigitHour);
+ rightAlt.setEnabled(validTwoDigitHour);
+ } else if (count() == 3) {
+ if (is24HourFormat()) {
+ // For the 24-hour clock, no access at all because
+ // two more digits (00 or 30) cannot be added to 3 digits.
+ leftAlt.setEnabled(false);
+ rightAlt.setEnabled(false);
+ } else {
+ // True for any 3 digits, if AM/PM not already entered
+ boolean enabled = mAmPmState == UNSPECIFIED;
+ leftAlt.setEnabled(enabled);
+ rightAlt.setEnabled(enabled);
+ }
+ } else if (count() == MAX_DIGITS) {
+ // If all 4 digits are filled in, the 24-hour clock has absolutely
+ // no need for the alt buttons. However, The 12-hour clock has
+ // complete need of them, if not already used.
+ boolean enabled = !is24HourFormat() && mAmPmState == UNSPECIFIED;
+ leftAlt.setEnabled(enabled);
+ rightAlt.setEnabled(enabled);
+ }
+ }
+
+ private void updateBackspaceState() {
+ setBackspaceEnabled(count() > 0);
+ }
+
+ private void updateFabState() {
+ // Special case of 0 digits is legal (that is the default hint time)
+ if (count() == 0) {
+ fab.setEnabled(true);
+ return;
+ }
+ // Minimum of 3 digits is required
+ if (count() < 3) {
+ fab.setEnabled(false);
+ return;
+ }
+
+ if (is24HourFormat()) {
+ int time = getInput();
+ fab.setEnabled(time % 100 <= 59);
+ } else {
+ // If on 12-hour clock, FAB will never be enabled
+ // until AM or PM is explicitly clicked.
+ fab.setEnabled(mAmPmState != UNSPECIFIED);
+ }
+ }
+
+ private void updateNumpadStates() {
+ updateAltButtonStates();
+ updateFabState();
+ updateBackspaceState();
+ updateNumberKeysStates();
+ }
+
+ private void updateNumberKeysStates() {
+ int cap = 10; // number of buttons
+ boolean is24hours = is24HourFormat();
+
+ if (count() == 0) {
+ enable(is24hours ? 0 : 1, cap);
+ return;
+ } else if (count() == MAX_DIGITS) {
+ enable(0, 0);
+ return;
+ }
+
+ int time = getInput();
+ if (is24hours) {
+ if (count() == 1) {
+ enable(0, time < 2 ? cap : 6);
+ } else if (count() == 2) {
+ enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 6);
+ } else if (count() == 3) {
+ if (time >= 236) {
+ enable(0, 0);
+ } else {
+ enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0);
+ }
+ }
+ } else {
+ if (count() == 1) {
+ if (time == 0) {
+ throw new IllegalStateException("12-hr format, zeroth digit = 0?");
+ } else {
+ enable(0, 6);
+ }
+ } else if (count() == 2 || count() == 3) {
+ if (time >= 126) {
+ enable(0, 0);
+ } else {
+ if (time >= 100 && time <= 125 && mAmPmState != UNSPECIFIED) {
+ // Could legally input fourth digit, if not for the am/pm state already set
+ enable(0, 0);
+ } else {
+ enable(0, time % 10 >= 0 && time % 10 <= 5 ? cap : 0);
+ }
+ }
+ }
+ }
+ }
+
+ // Helper method for inputting each character of an altBtn.
+ private void onAltButtonClick(Button altBtn) {
+ if (leftAlt != altBtn && rightAlt != altBtn)
+ throw new IllegalArgumentException("Not called with one of the alt buttons");
+
+ // Manually insert special characters for 12-hour clock
+ if (!is24HourFormat()) {
+ if (count() <= 2) {
+ // The colon is inserted for you
+ performClick(0);
+ performClick(0);
+ }
+ // text is AM or PM, so include space before
+ mFormattedInput.append(' ').append(altBtn.getText());
+ mAmPmState = leftAlt == altBtn ? AM : PM;
+ // Digits will be shown for you on click, but not AM/PM
+ notifyOnNumberInputListener(mFormattedInput.toString());
+ } else {
+ // Need to consider each character individually,
+ // as we are only interested in storing the digits
+ CharSequence text = altBtn.getText();
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ if (Character.isDigit(c)) {
+ // Convert char to digit and perform click
+ // Colon is added for you
+ performClick(c);
+ }
+ }
+ mAmPmState = HRS_24;
+ }
+
+ updateNumpadStates();
+ }
+
+ private boolean is24HourFormat() {
+ return DateFormat.is24HourFormat(getContext());
+ }
+
+ private void inputNumber(CharSequence number) {
+ mFormattedInput.append(number);
+ // Add colon if necessary, depending on how many digits entered so far
+ if (count() == 3) {
+ // Insert a colon
+ int digits = getInput();
+ if (digits >= 60 && digits < 100 || digits >= 160 && digits < 200) {
+ // From 060-099 (really only to 095, but might as well go up to 100)
+ // From 160-199 (really only to 195, but might as well go up to 200),
+ // time does not exist if colon goes at pos. 1
+ mFormattedInput.insert(2, ':');
+ // These times only apply to the 24-hour clock, and if we're here,
+ // the time is not legal yet. So we can't set mAmPmState here for
+ // either clock.
+ // The 12-hour clock can only have mAmPmState set when AM/PM are clicked.
+ } else {
+ // A valid time exists if colon is at pos. 1
+ mFormattedInput.insert(1, ':');
+ // We can set mAmPmState here (and not in the above case) because
+ // the time here is legal in 24-hour clock
+ if (is24HourFormat()) {
+ mAmPmState = HRS_24;
+ }
+ }
+ } else if (count() == MAX_DIGITS) {
+ // Colon needs to move, so remove the colon previously added
+ mFormattedInput.deleteCharAt(mFormattedInput.indexOf(":"));
+ mFormattedInput.insert(2, ':');
+
+ // Time is legal in 24-hour clock
+ if (is24HourFormat()) {
+ mAmPmState = HRS_24;
+ }
+ }
+
+ // Moved to onClick()
+ //notifyOnNumberInputListener(mFormattedInput.toString());
+ }
+
+ public interface KeyListener extends Numpad.KeyListener {
+ void onAcceptChanges();
+ }
+
+ private void init() {
+ // Build alternative action buttons
+ leftAlt = (Button) buildButton(R.layout.numpad_alt_button, 3, 0);
+ rightAlt = (Button) buildButton(R.layout.numpad_alt_button, 3, 2);
+ leftAlt.setText(is24HourFormat() ? R.string.left_alt_24hr : R.string.am);
+ rightAlt.setText(is24HourFormat() ? R.string.right_alt_24hr : R.string.pm);
+
+ leftAlt.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onAltButtonClick(leftAlt);
+ }
+ });
+
+ rightAlt.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onAltButtonClick(rightAlt);
+ }
+ });
+
+ newRow();
+ buildBackspace(getChildCount() - 1, 2);
+// buildCollapse(getChildCount() - 1, 0);
+ // The FAB is wrapped in a FrameLayout
+ FrameLayout frame = (FrameLayout)
+ buildButton(R.layout.numpad_fab, getChildCount() - 1, 1);
+ fab = (FloatingActionButton) frame.getChildAt(0);
+ //fab.setBackgroundTintList(getResources().getColorStateList(R.color.on_enabled_change_fab));
+
+ fab.setEnabled(false);
+
+ fab.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (count() == 0) {
+ // Default time
+ setTime(0, 0);
+ }
+ KeyListener kl;
+ try {
+ kl = (KeyListener) getKeyListener();
+ } catch (ClassCastException e) {
+ throw new ClassCastException("Using AlarmNumpad with Numpad.KeyListener instead of AlarmNumpad.KeyListener");
+ } catch (NullPointerException e) {
+ throw new NullPointerException("Numpad's KeyListener is not set");
+ }
+ kl.onAcceptChanges();
+ }
+ });
+
+ updateNumpadStates();
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java
index 9b5d6e8..fcb3ffa 100644
--- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java
+++ b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java
@@ -9,6 +9,8 @@ import android.support.v7.app.ActionBar;
import android.support.v7.widget.SwitchCompat;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
@@ -24,13 +26,15 @@ import java.util.Date;
import butterknife.Bind;
import butterknife.OnClick;
+import butterknife.OnTouch;
import static android.text.format.DateFormat.getTimeFormat;
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
import static com.philliphsu.clock2.DaysOfWeek.SUNDAY;
-public class EditAlarmActivity extends BaseActivity {
+public class EditAlarmActivity extends BaseActivity implements AlarmEditText.OnBackPressListener {
+ private static final String TAG = "EditAlarmActivity";
public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID";
private static final int REQUEST_PICK_RINGTONE = 0;
@@ -42,12 +46,13 @@ public class EditAlarmActivity extends BaseActivity {
@Bind(R.id.save) Button mSave;
@Bind(R.id.delete) Button mDelete;
@Bind(R.id.on_off) SwitchCompat mSwitch;
- @Bind(R.id.input_time) EditText mTimeText;
+ @Bind(R.id.input_time) AlarmEditText 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;
+ @Bind(R.id.numpad) AlarmNumpad mNumpad;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -76,8 +81,12 @@ public class EditAlarmActivity extends BaseActivity {
ab.setDisplayShowTitleEnabled(true);
ab.setTitle(title);
}
+ // Editing alarm so don't show
+ mNumpad.setVisibility(View.GONE);
} else {
// TODO default values
+ // No alarm retrieved with this id
+ mNumpad.setVisibility(View.VISIBLE);
}
} else {
// Initializing to Settings.System.DEFAULT_ALARM_ALERT_URI will show
@@ -87,8 +96,10 @@ public class EditAlarmActivity extends BaseActivity {
// Compare with getDefaultUri(int), which returns the symbolic URI instead of the
// actual sound URI. For TYPE_ALARM, this actually returns the same constant.
mSelectedRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
+ mNumpad.setVisibility(View.VISIBLE);
}
updateRingtoneButtonText();
+ mTimeText.setOnBackPressListener(this);
}
@Override
@@ -128,6 +139,19 @@ public class EditAlarmActivity extends BaseActivity {
return 0;
}
+ /** Handle back press when virtual keyboard is shown */
+ @Override
+ public void onBackPress() {
+ // TODO: Hide your numpad.
+ mNumpad.setVisibility(View.GONE);
+ }
+
+ @OnTouch(R.id.input_time)
+ boolean touch(MotionEvent event) {
+ mNumpad.setVisibility(View.VISIBLE);
+ return true;
+ }
+
@OnClick(R.id.ringtone)
void ringtone() {
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
diff --git a/app/src/main/res/color/on_enabled_change_fab.xml b/app/src/main/res/color/on_enabled_change_fab.xml
new file mode 100644
index 0000000..bbc8af3
--- /dev/null
+++ b/app/src/main/res/color/on_enabled_change_fab.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_backspace_24dp.xml b/app/src/main/res/drawable/ic_backspace_24dp.xml
new file mode 100644
index 0000000..54ad8c1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_backspace_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_done_24dp.xml b/app/src/main/res/drawable/ic_done_24dp.xml
new file mode 100644
index 0000000..fb13677
--- /dev/null
+++ b/app/src/main/res/drawable/ic_done_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_edit_alarm.xml b/app/src/main/res/layout/activity_edit_alarm.xml
index 495eac6..cb79d0e 100644
--- a/app/src/main/res/layout/activity_edit_alarm.xml
+++ b/app/src/main/res/layout/activity_edit_alarm.xml
@@ -74,7 +74,7 @@
android:layout_centerVertical="true"/>
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_numpad.xml b/app/src/main/res/layout/content_numpad.xml
new file mode 100644
index 0000000..6ba2957
--- /dev/null
+++ b/app/src/main/res/layout/content_numpad.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/numpad_alt_button.xml b/app/src/main/res/layout/numpad_alt_button.xml
new file mode 100644
index 0000000..972f97c
--- /dev/null
+++ b/app/src/main/res/layout/numpad_alt_button.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/numpad_backspace.xml b/app/src/main/res/layout/numpad_backspace.xml
new file mode 100644
index 0000000..58617db
--- /dev/null
+++ b/app/src/main/res/layout/numpad_backspace.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/numpad_fab.xml b/app/src/main/res/layout/numpad_fab.xml
new file mode 100644
index 0000000..1d56f55
--- /dev/null
+++ b/app/src/main/res/layout/numpad_fab.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0611111..2d181bf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,6 +26,22 @@
Fri
Sat
Every day
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ :00
+ :30
+ AM
+ PM
+
"Material is the metaphor.\n\n"
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 177cefc..7447189 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -17,4 +17,13 @@
+
+