267 lines
8.4 KiB
Java
267 lines
8.4 KiB
Java
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);
|
|
}
|
|
}
|