Clockplus/app/src/main/java/com/philliphsu/clock2/Numpad.java
2016-06-07 02:03:05 -07:00

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);
}
}