package com.codetroopers.betterpickers.hmspicker;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.codetroopers.betterpickers.R;
public class HmsPicker extends LinearLayout implements Button.OnClickListener, Button.OnLongClickListener {
protected int mInputSize = 5;
protected final Button mNumbers[] = new Button[10];
protected int mInput[] = new int[mInputSize];
protected int mInputPointer = -1;
protected ImageButton mDelete;
protected Button mLeft, mRight;
protected HmsView mEnteredHms;
protected final Context mContext;
private TextView mHoursLabel, mMinutesLabel, mSecondsLabel;
private Button mSetButton;
protected View mDivider;
private ColorStateList mTextColor;
private int mKeyBackgroundResId;
private int mButtonBackgroundResId;
private int mDividerColor;
private int mDeleteDrawableSrcResId;
private int mTheme = -1;
private int mSign;
public static final int SIGN_POSITIVE = 0;
public static final int SIGN_NEGATIVE = 1;
/**
* Instantiates an HmsPicker object
*
* @param context the Context required for creation
*/
public HmsPicker(Context context) {
this(context, null);
}
/**
* Instantiates an HmsPicker object
*
* @param context the Context required for creation
* @param attrs additional attributes that define custom colors, selectors, and backgrounds.
*/
public HmsPicker(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
LayoutInflater layoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(getLayoutId(), this);
// Init defaults
mTextColor = getResources().getColorStateList(R.color.dialog_text_color_holo_dark);
mKeyBackgroundResId = R.drawable.key_background_dark;
mButtonBackgroundResId = R.drawable.button_background_dark;
mDividerColor = getResources().getColor(R.color.default_divider_color_dark);
mDeleteDrawableSrcResId = R.drawable.ic_backspace_dark;
}
protected int getLayoutId() {
return R.layout.hms_picker_view;
}
/**
* Change the theme of the Picker
*
* @param themeResId the resource ID of the new style
*/
public void setTheme(int themeResId) {
mTheme = themeResId;
if (mTheme != -1) {
TypedArray a = getContext().obtainStyledAttributes(themeResId, R.styleable.BetterPickersDialogFragment);
mTextColor = a.getColorStateList(R.styleable.BetterPickersDialogFragment_bpTextColor);
mKeyBackgroundResId = a.getResourceId(R.styleable.BetterPickersDialogFragment_bpKeyBackground,
mKeyBackgroundResId);
mButtonBackgroundResId = a.getResourceId(R.styleable.BetterPickersDialogFragment_bpButtonBackground,
mButtonBackgroundResId);
mDividerColor = a.getColor(R.styleable.BetterPickersDialogFragment_bpDividerColor, mDividerColor);
mDeleteDrawableSrcResId = a.getResourceId(R.styleable.BetterPickersDialogFragment_bpDeleteIcon,
mDeleteDrawableSrcResId);
}
restyleViews();
}
private void restyleViews() {
for (Button number : mNumbers) {
if (number != null) {
number.setTextColor(mTextColor);
number.setBackgroundResource(mKeyBackgroundResId);
}
}
if (mDivider != null) {
mDivider.setBackgroundColor(mDividerColor);
}
if (mHoursLabel != null) {
mHoursLabel.setTextColor(mTextColor);
mHoursLabel.setBackgroundResource(mKeyBackgroundResId);
}
if (mMinutesLabel != null) {
mMinutesLabel.setTextColor(mTextColor);
mMinutesLabel.setBackgroundResource(mKeyBackgroundResId);
}
if (mSecondsLabel != null) {
mSecondsLabel.setTextColor(mTextColor);
mSecondsLabel.setBackgroundResource(mKeyBackgroundResId);
}
if (mDelete != null) {
mDelete.setBackgroundResource(mButtonBackgroundResId);
mDelete.setImageDrawable(getResources().getDrawable(mDeleteDrawableSrcResId));
}
if (mEnteredHms != null) {
mEnteredHms.setTheme(mTheme);
}
if (mLeft != null) {
mLeft.setTextColor(mTextColor);
mLeft.setBackgroundResource(mKeyBackgroundResId);
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
View v1 = findViewById(R.id.first);
View v2 = findViewById(R.id.second);
View v3 = findViewById(R.id.third);
View v4 = findViewById(R.id.fourth);
mEnteredHms = (HmsView) findViewById(R.id.hms_text);
mDelete = (ImageButton) findViewById(R.id.delete);
mDelete.setOnClickListener(this);
mDelete.setOnLongClickListener(this);
mNumbers[1] = (Button) v1.findViewById(R.id.key_left);
mNumbers[2] = (Button) v1.findViewById(R.id.key_middle);
mNumbers[3] = (Button) v1.findViewById(R.id.key_right);
mNumbers[4] = (Button) v2.findViewById(R.id.key_left);
mNumbers[5] = (Button) v2.findViewById(R.id.key_middle);
mNumbers[6] = (Button) v2.findViewById(R.id.key_right);
mNumbers[7] = (Button) v3.findViewById(R.id.key_left);
mNumbers[8] = (Button) v3.findViewById(R.id.key_middle);
mNumbers[9] = (Button) v3.findViewById(R.id.key_right);
mLeft = (Button) v4.findViewById(R.id.key_left);
mNumbers[0] = (Button) v4.findViewById(R.id.key_middle);
mRight = (Button) v4.findViewById(R.id.key_right);
setRightEnabled(false);
for (int i = 0; i < 10; i++) {
mNumbers[i].setOnClickListener(this);
mNumbers[i].setText(String.format("%d", i));
mNumbers[i].setTag(R.id.numbers_key, new Integer(i));
}
updateHms();
Resources res = mContext.getResources();
mLeft.setText(res.getString(R.string.number_picker_plus_minus));
mLeft.setOnClickListener(this);
mHoursLabel = (TextView) findViewById(R.id.hours_label);
mMinutesLabel = (TextView) findViewById(R.id.minutes_label);
mSecondsLabel = (TextView) findViewById(R.id.seconds_label);
mDivider = findViewById(R.id.divider);
restyleViews();
updateKeypad();
}
/**
* Update the delete button to determine whether it is able to be clicked.
*/
public void updateDeleteButton() {
boolean enabled = mInputPointer != -1;
if (mDelete != null) {
mDelete.setEnabled(enabled);
}
}
@Override
public void onClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
doOnClick(v);
updateDeleteButton();
}
protected void doOnClick(View v) {
Integer val = (Integer) v.getTag(R.id.numbers_key);
// A number was pressed
if (val != null) {
addClickedNumber(val);
} else if (v == mDelete) {
if (mInputPointer >= 0) {
for (int i = 0; i < mInputPointer; i++) {
mInput[i] = mInput[i + 1];
}
mInput[mInputPointer] = 0;
mInputPointer--;
}
} else if (v == mLeft) {
onLeftClicked();
}
updateKeypad();
}
private void onLeftClicked() {
if (isNegative()) {
mSign = SIGN_POSITIVE;
} else {
mSign = SIGN_NEGATIVE;
}
}
@Override
public boolean onLongClick(View v) {
v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
if (v == mDelete) {
mDelete.setPressed(false);
reset();
updateKeypad();
return true;
}
return false;
}
/**
* Reset all inputs and the hours:minutes:seconds.
*/
public void reset() {
for (int i = 0; i < mInputSize; i++) {
mInput[i] = 0;
}
mInputPointer = -1;
updateHms();
}
private void updateKeypad() {
// Update the h:m:s
updateHms();
// enable/disable the "set" key
enableSetButton();
// Update the backspace button
updateDeleteButton();
}
/**
* Update the time displayed in the picker:
* <p/>
* Put "-" in digits that was not entered by passing -1
* <p/>
* Hide digit by passing -2 (for highest hours digit only);
*/
protected void updateHms() {
mEnteredHms.setTime(isNegative(), mInput[4], mInput[3], mInput[2], mInput[1], mInput[0]);
}
private void addClickedNumber(int val) {
if (mInputPointer < mInputSize - 1 && !(mInputPointer == -1 && val == 0)) {
for (int i = mInputPointer; i >= 0; i--) {
mInput[i + 1] = mInput[i];
}
mInputPointer++;
mInput[0] = val;
}
}
/**
* Enable/disable the "Set" button
*/
private void enableSetButton() {
if (mSetButton == null) {
return;
}
// Nothing entered - disable
if (mInputPointer == -1) {
mSetButton.setEnabled(false);
return;
}
mSetButton.setEnabled(mInputPointer >= 0);
}
/**
* Expose the set button to allow communication with the parent Fragment.
*
* @param b the parent Fragment's "Set" button
*/
public void setSetButton(Button b) {
mSetButton = b;
enableSetButton();
}
/**
* Returns the hours as currently inputted by the user.
*
* @return the inputted hours
*/
public int getHours() {
int hours = mInput[4];
return hours;
}
/**
* Returns the minutes as currently inputted by the user.
*
* @return the inputted minutes
*/
public int getMinutes() {
return mInput[3] * 10 + mInput[2];
}
/**
* Return the seconds as currently inputted by the user.
*
* @return the inputted seconds
*/
public int getSeconds() {
return mInput[1] * 10 + mInput[0];
}
/**
* Using View.GONE, View.VISIBILE, or View.INVISIBLE, set the visibility of the plus/minus indicator
*
* @param visibility an int using Android's View.* convention
*/
public void setPlusMinusVisibility(int visibility) {
if (mLeft != null) {
mLeft.setVisibility(visibility);
}
}
/**
* Set the current hours, minutes, and seconds on the picker.
*
* @param hours the input hours value
* @param minutes the input minutes value
* @param seconds the input seconds value
*/
public void setTime(int hours, int minutes, int seconds) {
mInput[4] = hours;
mInput[3] = minutes / 10;
mInput[2] = minutes % 10;
mInput[1] = seconds / 10;
mInput[0] = seconds % 10;
for (int i = 4; i >= 0; i--) {
if (mInput[i] > 0) {
mInputPointer = i;
break;
}
}
updateKeypad();
}
@Override
public Parcelable onSaveInstanceState() {
final Parcelable parcel = super.onSaveInstanceState();
final SavedState state = new SavedState(parcel);
state.mInput = mInput;
state.mInputPointer = mInputPointer;
return state;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
final SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mInputPointer = savedState.mInputPointer;
mInput = savedState.mInput;
if (mInput == null) {
mInput = new int[mInputSize];
mInputPointer = -1;
}
updateKeypad();
}
private static class SavedState extends BaseSavedState {
int mInputPointer;
int[] mInput;
int mAmPmState;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
mInputPointer = in.readInt();
mInput = in.createIntArray();
mAmPmState = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mInputPointer);
dest.writeIntArray(mInput);
dest.writeInt(mAmPmState);
}
public static final Creator<SavedState> CREATOR
= new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
/**
* Returns the time in seconds
*
* @return an int representing the time in seconds
*/
public int getTime() {
return mInput[4] * 3600 + mInput[3] * 600 + mInput[2] * 60 + mInput[1] * 10 + mInput[0];
}
public void saveEntryState(Bundle outState, String key) {
outState.putIntArray(key, mInput);
}
public void restoreEntryState(Bundle inState, String key) {
int[] input = inState.getIntArray(key);
if (input != null && mInputSize == input.length) {
for (int i = 0; i < mInputSize; i++) {
mInput[i] = input[i];
if (mInput[i] != 0) {
mInputPointer = i;
}
}
updateHms();
}
}
protected void setRightEnabled(boolean enabled) {
mRight.setEnabled(enabled);
if (!enabled) {
mRight.setContentDescription(null);
}
}
public boolean isNegative() {
return mSign == SIGN_NEGATIVE;
}
}