Ported numpad
This commit is contained in:
parent
680fd28782
commit
881b4dcf83
@ -41,7 +41,8 @@
|
|||||||
android:name=".editalarm.EditAlarmActivity"
|
android:name=".editalarm.EditAlarmActivity"
|
||||||
android:label="@string/title_activity_edit_alarm"
|
android:label="@string/title_activity_edit_alarm"
|
||||||
android:parentActivityName=".MainActivity"
|
android:parentActivityName=".MainActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
|
android:windowSoftInputMode="adjustNothing">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="com.philliphsu.clock2.MainActivity"/>
|
android:value="com.philliphsu.clock2.MainActivity"/>
|
||||||
|
|||||||
254
app/src/main/java/com/philliphsu/clock2/Numpad.java
Normal file
254
app/src/main/java/com/philliphsu/clock2/Numpad.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,8 @@ import android.support.v7.app.ActionBar;
|
|||||||
import android.support.v7.widget.SwitchCompat;
|
import android.support.v7.widget.SwitchCompat;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
@ -24,13 +26,15 @@ import java.util.Date;
|
|||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
|
import butterknife.OnTouch;
|
||||||
|
|
||||||
import static android.text.format.DateFormat.getTimeFormat;
|
import static android.text.format.DateFormat.getTimeFormat;
|
||||||
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
|
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
|
||||||
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
|
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
|
||||||
import static com.philliphsu.clock2.DaysOfWeek.SUNDAY;
|
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";
|
public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID";
|
||||||
|
|
||||||
private static final int REQUEST_PICK_RINGTONE = 0;
|
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.save) Button mSave;
|
||||||
@Bind(R.id.delete) Button mDelete;
|
@Bind(R.id.delete) Button mDelete;
|
||||||
@Bind(R.id.on_off) SwitchCompat mSwitch;
|
@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})
|
@Bind({R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6})
|
||||||
ToggleButton[] mDays;
|
ToggleButton[] mDays;
|
||||||
@Bind(R.id.label) EditText mLabel;
|
@Bind(R.id.label) EditText mLabel;
|
||||||
@Bind(R.id.ringtone) Button mRingtone;
|
@Bind(R.id.ringtone) Button mRingtone;
|
||||||
@Bind(R.id.vibrate) CheckBox mVibrate;
|
@Bind(R.id.vibrate) CheckBox mVibrate;
|
||||||
|
@Bind(R.id.numpad) AlarmNumpad mNumpad;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -76,8 +81,12 @@ public class EditAlarmActivity extends BaseActivity {
|
|||||||
ab.setDisplayShowTitleEnabled(true);
|
ab.setDisplayShowTitleEnabled(true);
|
||||||
ab.setTitle(title);
|
ab.setTitle(title);
|
||||||
}
|
}
|
||||||
|
// Editing alarm so don't show
|
||||||
|
mNumpad.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
// TODO default values
|
// TODO default values
|
||||||
|
// No alarm retrieved with this id
|
||||||
|
mNumpad.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Initializing to Settings.System.DEFAULT_ALARM_ALERT_URI will show
|
// 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
|
// Compare with getDefaultUri(int), which returns the symbolic URI instead of the
|
||||||
// actual sound URI. For TYPE_ALARM, this actually returns the same constant.
|
// actual sound URI. For TYPE_ALARM, this actually returns the same constant.
|
||||||
mSelectedRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
|
mSelectedRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
|
||||||
|
mNumpad.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
updateRingtoneButtonText();
|
updateRingtoneButtonText();
|
||||||
|
mTimeText.setOnBackPressListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -128,6 +139,19 @@ public class EditAlarmActivity extends BaseActivity {
|
|||||||
return 0;
|
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)
|
@OnClick(R.id.ringtone)
|
||||||
void ringtone() {
|
void ringtone() {
|
||||||
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
||||||
|
|||||||
6
app/src/main/res/color/on_enabled_change_fab.xml
Normal file
6
app/src/main/res/color/on_enabled_change_fab.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="@color/colorAccent" android:state_enabled="true"/>
|
||||||
|
<item android:color="@android:color/darker_gray" android:state_enabled="false"/>
|
||||||
|
<item android:color="@android:color/darker_gray"/>
|
||||||
|
</selector>
|
||||||
9
app/src/main/res/drawable/ic_backspace_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_backspace_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-3,12.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_done_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_done_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||||
|
</vector>
|
||||||
@ -74,7 +74,7 @@
|
|||||||
android:layout_centerVertical="true"/>
|
android:layout_centerVertical="true"/>
|
||||||
|
|
||||||
<!-- TODO: Find out how to get a touch ripple; foreground attr didn't work -->
|
<!-- TODO: Find out how to get a touch ripple; foreground attr didn't work -->
|
||||||
<EditText
|
<com.philliphsu.clock2.editalarm.AlarmEditText
|
||||||
android:id="@+id/input_time"
|
android:id="@+id/input_time"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -191,4 +191,14 @@
|
|||||||
|
|
||||||
</ScrollView>
|
</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>
|
</RelativeLayout>
|
||||||
57
app/src/main/res/layout/content_numpad.xml
Normal file
57
app/src/main/res/layout/content_numpad.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ The children of a TableLayout cannot specify the layout_width attribute.
|
||||||
|
~ Width is always MATCH_PARENT. However, the layout_height attribute can be
|
||||||
|
~ defined by a child; default value is WRAP_CONTENT. If the child is a
|
||||||
|
~ TableRow, then the height is always WRAP_CONTENT.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/one"
|
||||||
|
android:text="@string/one"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/two"
|
||||||
|
android:text="@string/two"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/three"
|
||||||
|
android:text="@string/three"/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/four"
|
||||||
|
android:text="@string/four"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/five"
|
||||||
|
android:text="@string/five"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/six"
|
||||||
|
android:text="@string/six"/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/seven"
|
||||||
|
android:text="@string/seven"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/eight"
|
||||||
|
android:text="@string/eight"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/nine"
|
||||||
|
android:text="@string/nine"/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<Space android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
<Button style="@style/NumberButton"
|
||||||
|
android:id="@+id/zero"
|
||||||
|
android:text="@string/zero"/>
|
||||||
|
<Space android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
</merge>
|
||||||
2
app/src/main/res/layout/numpad_alt_button.xml
Normal file
2
app/src/main/res/layout/numpad_alt_button.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Button style="@style/NumberButton"/>
|
||||||
6
app/src/main/res/layout/numpad_backspace.xml
Normal file
6
app/src/main/res/layout/numpad_backspace.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
style="@style/NumberButton"
|
||||||
|
android:src="@drawable/ic_backspace_24dp"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark"
|
||||||
|
android:id="@+id/backspace"/>
|
||||||
14
app/src/main/res/layout/numpad_fab.xml
Normal file
14
app/src/main/res/layout/numpad_fab.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
style="@style/NumberButton"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_done_24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:clickable="true"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@ -26,6 +26,22 @@
|
|||||||
<string name="fri">Fri</string>
|
<string name="fri">Fri</string>
|
||||||
<string name="sat">Sat</string>
|
<string name="sat">Sat</string>
|
||||||
<string name="every_day">Every day</string>
|
<string name="every_day">Every day</string>
|
||||||
|
|
||||||
|
<string name="zero">0</string>
|
||||||
|
<string name="one">1</string>
|
||||||
|
<string name="two">2</string>
|
||||||
|
<string name="three">3</string>
|
||||||
|
<string name="four">4</string>
|
||||||
|
<string name="five">5</string>
|
||||||
|
<string name="six">6</string>
|
||||||
|
<string name="seven">7</string>
|
||||||
|
<string name="eight">8</string>
|
||||||
|
<string name="nine">9</string>
|
||||||
|
<string name="left_alt_24hr">:00</string>
|
||||||
|
<string name="right_alt_24hr">:30</string>
|
||||||
|
<string name="am">AM</string>
|
||||||
|
<string name="pm">PM</string>
|
||||||
|
|
||||||
<string name="large_text">
|
<string name="large_text">
|
||||||
"Material is the metaphor.\n\n"
|
"Material is the metaphor.\n\n"
|
||||||
|
|
||||||
|
|||||||
@ -17,4 +17,13 @@
|
|||||||
|
|
||||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||||
|
|
||||||
|
<style name="NumberButton">
|
||||||
|
<item name="android:layout_width">0dp</item>
|
||||||
|
<item name="android:layout_height">match_parent</item>
|
||||||
|
<item name="android:layout_weight">1</item>
|
||||||
|
<item name="android:padding">12dp</item>
|
||||||
|
<item name="android:background">?android:attr/selectableItemBackground</item>
|
||||||
|
<item name="android:textSize">28sp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user