diff --git a/library/src/main/java/com/codetroopers/betterpickers/numberpicker/NumberPicker.java b/library/src/main/java/com/codetroopers/betterpickers/numberpicker/NumberPicker.java index f07107b2..9da333e3 100644 --- a/library/src/main/java/com/codetroopers/betterpickers/numberpicker/NumberPicker.java +++ b/library/src/main/java/com/codetroopers/betterpickers/numberpicker/NumberPicker.java @@ -15,624 +15,617 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; - import com.codetroopers.betterpickers.R; - import java.math.BigDecimal; import java.math.BigInteger; import java.text.DecimalFormat; -public class NumberPicker extends LinearLayout implements Button.OnClickListener, - Button.OnLongClickListener { - - protected int mInputSize = 20; - protected final Button mNumbers[] = new Button[10]; - protected int mInput[] = new int[mInputSize]; - protected int mInputPointer = -1; - protected Button mLeft, mRight; - protected ImageButton mDelete; - protected NumberView mEnteredNumber; - protected final Context mContext; - - private TextView mLabel; - private NumberPickerErrorTextView mError; - private int mSign; - private String mLabelText = ""; - private Button mSetButton; - private static final int CLICKED_DECIMAL = 10; - - public static final int SIGN_POSITIVE = 0; - public static final int SIGN_NEGATIVE = 1; - - protected View mDivider; - private ColorStateList mTextColor; - private int mKeyBackgroundResId; - private int mButtonBackgroundResId; - private int mDividerColor; - private int mDeleteDrawableSrcResId; - private int mTheme = -1; - - private BigDecimal mMinNumber = null; - private BigDecimal mMaxNumber = null; - - /** - * Instantiates a NumberPicker object - * - * @param context the Context required for creation - */ - public NumberPicker(Context context) { - this(context, null); - } - - /** - * Instantiates a NumberPicker object - * - * @param context the Context required for creation - * @param attrs additional attributes that define custom colors, selectors, and backgrounds. - */ - public NumberPicker(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; - mDeleteDrawableSrcResId = R.drawable.ic_backspace_dark; - mDividerColor = getResources().getColor(R.color.default_divider_color_dark); - } - - protected int getLayoutId() { - return R.layout.number_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 (mLeft != null) { - mLeft.setTextColor(mTextColor); - mLeft.setBackgroundResource(mKeyBackgroundResId); - } - if (mRight != null) { - mRight.setTextColor(mTextColor); - mRight.setBackgroundResource(mKeyBackgroundResId); - } - if (mDelete != null) { - mDelete.setBackgroundResource(mButtonBackgroundResId); - mDelete.setImageDrawable(getResources().getDrawable(mDeleteDrawableSrcResId)); - } - if (mEnteredNumber != null) { - mEnteredNumber.setTheme(mTheme); - } - if (mLabel != null) { - mLabel.setTextColor(mTextColor); - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mDivider = findViewById(R.id.divider); - mError = (NumberPickerErrorTextView) findViewById(R.id.error); - - for (int i = 0; i < mInput.length; i++) { - mInput[i] = -1; - } - - View v1 = findViewById(R.id.first); - View v2 = findViewById(R.id.second); - View v3 = findViewById(R.id.third); - View v4 = findViewById(R.id.fourth); - mEnteredNumber = (NumberView) findViewById(R.id.number_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); - setLeftRightEnabled(); - - 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)); - } - updateNumber(); - - Resources res = mContext.getResources(); - mLeft.setText(res.getString(R.string.number_picker_plus_minus)); - mRight.setText(res.getString(R.string.number_picker_seperator)); - mLeft.setOnClickListener(this); - mRight.setOnClickListener(this); - mLabel = (TextView) findViewById(R.id.label); - mSign = SIGN_POSITIVE; - - // Set the correct label state - showLabel(); - - restyleViews(); - updateKeypad(); - } - - /** - * Using View.GONE, View.VISIBILE, or View.INVISIBLE, set the visibility of the plus/minus indicator - * - * @param visiblity an int using Android's View.* convention - */ - public void setPlusMinusVisibility(int visiblity) { - if (mLeft != null) { - mLeft.setVisibility(visiblity); - } - } - - /** - * Using View.GONE, View.VISIBILE, or View.INVISIBLE, set the visibility of the decimal indicator - * - * @param visiblity an int using Android's View.* convention - */ - public void setDecimalVisibility(int visiblity) { - if (mRight != null) { - mRight.setVisibility(visiblity); - } - } - - /** - * Set a minimum required number - * - * @param min the minimum required number - */ - public void setMin(BigDecimal min) { - mMinNumber = min; - } - - /** - * Set a maximum required number - * - * @param max the maximum required number - */ - public void setMax(BigDecimal max) { - mMaxNumber = max; - } - - /** - * 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); - } - } - - /** - * Expose the NumberView in order to set errors - * - * @return the NumberView - */ - public NumberPickerErrorTextView getErrorView() { - return mError; - } - - @Override - public void onClick(View v) { - v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); - mError.hideImmediately(); - doOnClick(v); - updateDeleteButton(); - } - - protected void doOnClick(View v) { - Integer val = (Integer) v.getTag(R.id.numbers_key); - if (val != null) { - // A number was pressed - addClickedNumber(val); - } else if (v == mDelete) { - if (mInputPointer >= 0) { - for (int i = 0; i < mInputPointer; i++) { - mInput[i] = mInput[i + 1]; - } - mInput[mInputPointer] = -1; - mInputPointer--; - } - } else if (v == mLeft) { - onLeftClicked(); - } else if (v == mRight) { - onRightClicked(); - } - updateKeypad(); - } - - @Override - public boolean onLongClick(View v) { - v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - mError.hideImmediately(); - if (v == mDelete) { - mDelete.setPressed(false); - reset(); - updateKeypad(); - return true; - } - return false; - } - - private void updateKeypad() { - // Update state of keypad - // Update the number - updateLeftRightButtons(); - updateNumber(); - // enable/disable the "set" key - enableSetButton(); - // Update the backspace button - updateDeleteButton(); - } - - /** - * Set the text displayed in the small label - * - * @param labelText the String to set as the label - */ - public void setLabelText(String labelText) { - mLabelText = labelText; - showLabel(); - } - - private void showLabel() { - if (mLabel != null) { - mLabel.setText(mLabelText); - } - } - - /** - * Reset all inputs. - */ - public void reset() { - for (int i = 0; i < mInputSize; i++) { - mInput[i] = -1; - } - mInputPointer = -1; - updateNumber(); - } - - // Update the number displayed in the picker: - protected void updateNumber() { - String numberString = getEnteredNumberString(); - numberString = numberString.replaceAll("\\-", ""); - String[] split = numberString.split("\\."); - if (split.length >= 2) { - if (split[0].equals("")) { - mEnteredNumber.setNumber("0", split[1], containsDecimal(), - mSign == SIGN_NEGATIVE); - } else { - mEnteredNumber.setNumber(split[0], split[1], containsDecimal(), - mSign == SIGN_NEGATIVE); - } - } else if (split.length == 1) { - mEnteredNumber.setNumber(split[0], "", containsDecimal(), - mSign == SIGN_NEGATIVE); - } else if (numberString.equals(".")) { - mEnteredNumber.setNumber("0", "", true, mSign == SIGN_NEGATIVE); - } - } - - protected void setLeftRightEnabled() { - mLeft.setEnabled(true); - mRight.setEnabled(canAddDecimal()); - if (!canAddDecimal()) { - mRight.setContentDescription(null); - } - } - - private void addClickedNumber(int val) { - if (mInputPointer < mInputSize - 1) { - // For 0 we need to check if we have a value of zero or not - if (mInput[0] == 0 && mInput[1] == -1 && !containsDecimal() && val != CLICKED_DECIMAL) { - mInput[0] = val; - } else { - for (int i = mInputPointer; i >= 0; i--) { - mInput[i + 1] = mInput[i]; - } - mInputPointer++; - mInput[0] = val; - } - } - } - - /** - * Clicking on the bottom left button will toggle the sign. - */ - private void onLeftClicked() { - if (mSign == SIGN_POSITIVE) { - mSign = SIGN_NEGATIVE; - } else { - mSign = SIGN_POSITIVE; - } - } - - /** - * Clicking on the bottom right button will add a decimal point. - */ - private void onRightClicked() { - if (canAddDecimal()) { - addClickedNumber(CLICKED_DECIMAL); - } - } - - private boolean containsDecimal() { - boolean containsDecimal = false; - for (int i : mInput) { - if (i == 10) { - containsDecimal = true; - } - } - return containsDecimal; - } - - /** - * Checks if the user allowed to click on the right button. - * - * @return true or false if the user is able to add a decimal or not - */ - private boolean canAddDecimal() { - return !containsDecimal(); - } - - private String getEnteredNumberString() { - String value = ""; +public class NumberPicker extends LinearLayout + implements Button.OnClickListener, Button.OnLongClickListener { + + protected int mInputSize = 20; + protected final Button mNumbers[] = new Button[10]; + protected int mInput[] = new int[mInputSize]; + protected int mInputPointer = -1; + protected Button mLeft, mRight; + protected ImageButton mDelete; + protected NumberView mEnteredNumber; + protected final Context mContext; + + private TextView mLabel; + private NumberPickerErrorTextView mError; + private int mSign; + private String mLabelText = ""; + private Button mSetButton; + private static final int CLICKED_DECIMAL = 10; + + public static final int SIGN_POSITIVE = 0; + public static final int SIGN_NEGATIVE = 1; + + protected View mDivider; + private ColorStateList mTextColor; + private int mKeyBackgroundResId; + private int mButtonBackgroundResId; + private int mDividerColor; + private int mDeleteDrawableSrcResId; + private int mTheme = -1; + + private BigDecimal mMinNumber = null; + private BigDecimal mMaxNumber = null; + + /** + * Instantiates a NumberPicker object + * + * @param context the Context required for creation + */ + public NumberPicker(Context context) { + this(context, null); + } + + /** + * Instantiates a NumberPicker object + * + * @param context the Context required for creation + * @param attrs additional attributes that define custom colors, selectors, and backgrounds. + */ + public NumberPicker(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; + mDeleteDrawableSrcResId = R.drawable.ic_backspace_dark; + mDividerColor = getResources().getColor(R.color.default_divider_color_dark); + } + + protected int getLayoutId() { + return R.layout.number_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 (mLeft != null) { + mLeft.setTextColor(mTextColor); + mLeft.setBackgroundResource(mKeyBackgroundResId); + } + if (mRight != null) { + mRight.setTextColor(mTextColor); + mRight.setBackgroundResource(mKeyBackgroundResId); + } + if (mDelete != null) { + mDelete.setBackgroundResource(mButtonBackgroundResId); + mDelete.setImageDrawable(getResources().getDrawable(mDeleteDrawableSrcResId)); + } + if (mEnteredNumber != null) { + mEnteredNumber.setTheme(mTheme); + } + if (mLabel != null) { + mLabel.setTextColor(mTextColor); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mDivider = findViewById(R.id.divider); + mError = (NumberPickerErrorTextView) findViewById(R.id.error); + + for (int i = 0; i < mInput.length; i++) { + mInput[i] = -1; + } + + View v1 = findViewById(R.id.first); + View v2 = findViewById(R.id.second); + View v3 = findViewById(R.id.third); + View v4 = findViewById(R.id.fourth); + mEnteredNumber = (NumberView) findViewById(R.id.number_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); + setLeftRightEnabled(); + + 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)); + } + updateNumber(); + + Resources res = mContext.getResources(); + mLeft.setText(res.getString(R.string.number_picker_plus_minus)); + mRight.setText(res.getString(R.string.number_picker_seperator)); + mLeft.setOnClickListener(this); + mRight.setOnClickListener(this); + mLabel = (TextView) findViewById(R.id.label); + mSign = SIGN_POSITIVE; + + // Set the correct label state + showLabel(); + + restyleViews(); + updateKeypad(); + } + + /** + * Using View.GONE, View.VISIBILE, or View.INVISIBLE, set the visibility of the plus/minus + * indicator + * + * @param visiblity an int using Android's View.* convention + */ + public void setPlusMinusVisibility(int visiblity) { + if (mLeft != null) { + mLeft.setVisibility(visiblity); + } + } + + /** + * Using View.GONE, View.VISIBILE, or View.INVISIBLE, set the visibility of the decimal indicator + * + * @param visiblity an int using Android's View.* convention + */ + public void setDecimalVisibility(int visiblity) { + if (mRight != null) { + mRight.setVisibility(visiblity); + } + } + + /** + * Set a minimum required number + * + * @param min the minimum required number + */ + public void setMin(BigDecimal min) { + mMinNumber = min; + } + + /** + * Set a maximum required number + * + * @param max the maximum required number + */ + public void setMax(BigDecimal max) { + mMaxNumber = max; + } + + /** 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); + } + } + + /** + * Expose the NumberView in order to set errors + * + * @return the NumberView + */ + public NumberPickerErrorTextView getErrorView() { + return mError; + } + + @Override + public void onClick(View v) { + v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + mError.hideImmediately(); + doOnClick(v); + updateDeleteButton(); + } + + protected void doOnClick(View v) { + Integer val = (Integer) v.getTag(R.id.numbers_key); + if (val != null) { + // A number was pressed + addClickedNumber(val); + } else if (v == mDelete) { + if (mInputPointer >= 0) { + for (int i = 0; i < mInputPointer; i++) { + mInput[i] = mInput[i + 1]; + } + mInput[mInputPointer] = -1; + mInputPointer--; + } + } else if (v == mLeft) { + onLeftClicked(); + } else if (v == mRight) { + onRightClicked(); + } + updateKeypad(); + } + + @Override + public boolean onLongClick(View v) { + v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + mError.hideImmediately(); + if (v == mDelete) { + mDelete.setPressed(false); + reset(); + updateKeypad(); + return true; + } + return false; + } + + private void updateKeypad() { + // Update state of keypad + // Update the number + updateLeftRightButtons(); + updateNumber(); + // enable/disable the "set" key + enableSetButton(); + // Update the backspace button + updateDeleteButton(); + } + + /** + * Set the text displayed in the small label + * + * @param labelText the String to set as the label + */ + public void setLabelText(String labelText) { + mLabelText = labelText; + showLabel(); + } + + private void showLabel() { + if (mLabel != null) { + mLabel.setText(mLabelText); + } + } + + /** Reset all inputs. */ + public void reset() { + for (int i = 0; i < mInputSize; i++) { + mInput[i] = -1; + } + mInputPointer = -1; + updateNumber(); + } + + // Update the number displayed in the picker: + protected void updateNumber() { + String numberString = getEnteredNumberString(); + numberString = numberString.replaceAll("\\-", ""); + String[] split = numberString.split("\\."); + if (split.length >= 2) { + if (split[0].equals("")) { + mEnteredNumber.setNumber("0", split[1], containsDecimal(), mSign == SIGN_NEGATIVE); + } else { + mEnteredNumber.setNumber(split[0], split[1], containsDecimal(), mSign == SIGN_NEGATIVE); + } + } else if (split.length == 1) { + mEnteredNumber.setNumber(split[0], "", containsDecimal(), mSign == SIGN_NEGATIVE); + } else if (numberString.equals(".")) { + mEnteredNumber.setNumber("0", "", true, mSign == SIGN_NEGATIVE); + } + } + + protected void setLeftRightEnabled() { + mLeft.setEnabled(true); + mRight.setEnabled(canAddDecimal()); + if (!canAddDecimal()) { + mRight.setContentDescription(null); + } + } + + private void addClickedNumber(int val) { + if (mInputPointer < mInputSize - 1) { + // For 0 we need to check if we have a value of zero or not + if (mInput[0] == 0 && mInput[1] == -1 && !containsDecimal() && val != CLICKED_DECIMAL) { + mInput[0] = val; + } else { for (int i = mInputPointer; i >= 0; i--) { - if (mInput[i] == -1) { - // Don't add - } else if (mInput[i] == CLICKED_DECIMAL) { - value += "."; - } else { - value += mInput[i]; - } - } - return value; - } - - /** - * Returns the number inputted by the user - * - * @return a double representing the entered number - */ - public BigDecimal getEnteredNumber() { - String value = "0"; - for (int i = mInputPointer; i >= 0; i--) { - if (mInput[i] == -1) { - break; - } else if (mInput[i] == CLICKED_DECIMAL) { - value += "."; - } else { - value += mInput[i]; - } - } - if (mSign == SIGN_NEGATIVE) { - value = "-" + value; - } - - return new BigDecimal(value); - } - - private void updateLeftRightButtons() { - mRight.setEnabled(canAddDecimal()); - } - - /** - * Enable/disable the "Set" button - */ - private void enableSetButton() { - if (mSetButton == null) { - return; - } - - // Nothing entered - disable - if (mInputPointer == -1) { - mSetButton.setEnabled(false); - return; - } - - // If the user entered 1 digits or more - 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 number as currently inputted by the user - * - * @return an String representation of the number with no decimal - */ - public BigInteger getNumber() { - BigDecimal bigDecimal = getEnteredNumber().setScale(0, BigDecimal.ROUND_FLOOR); - return bigDecimal.toBigIntegerExact(); - } - - /** - * Returns the decimal following the number - * - * @return a double representation of the decimal value - */ - public double getDecimal() { - return getEnteredNumber().remainder(BigDecimal.ONE).doubleValue(); - } - - /** - * Returns whether the number is positive or negative - * - * @return true or false whether the number is positive or negative - */ - public boolean getIsNegative() { - return mSign == SIGN_NEGATIVE; - } - - @Override - public Parcelable onSaveInstanceState() { - final Parcelable parcel = super.onSaveInstanceState(); - final SavedState state = new SavedState(parcel); - state.mInput = mInput; - state.mSign = mSign; - state.mInputPointer = mInputPointer; - return state; + mInput[i + 1] = mInput[i]; + } + mInputPointer++; + mInput[0] = val; + } + } + } + + /** Clicking on the bottom left button will toggle the sign. */ + private void onLeftClicked() { + if (mSign == SIGN_POSITIVE) { + mSign = SIGN_NEGATIVE; + } else { + mSign = SIGN_POSITIVE; + } + } + + /** Clicking on the bottom right button will add a decimal point. */ + private void onRightClicked() { + if (canAddDecimal()) { + addClickedNumber(CLICKED_DECIMAL); + } + } + + private boolean containsDecimal() { + boolean containsDecimal = false; + for (int i : mInput) { + if (i == 10) { + containsDecimal = true; + } + } + return containsDecimal; + } + + /** + * Checks if the user allowed to click on the right button. + * + * @return true or false if the user is able to add a decimal or not + */ + private boolean canAddDecimal() { + return !containsDecimal(); + } + + private String getEnteredNumberString() { + String value = ""; + for (int i = mInputPointer; i >= 0; i--) { + if (mInput[i] == -1) { + // Don't add + } else if (mInput[i] == CLICKED_DECIMAL) { + value += "."; + } else { + value += mInput[i]; + } + } + return value; + } + + /** + * Returns the number inputted by the user + * + * @return a double representing the entered number + */ + public BigDecimal getEnteredNumber() { + StringBuilder value = new StringBuilder("0"); + for (int i = mInputPointer; i >= 0; i--) { + if (mInput[i] == -1) { + break; + } else if (mInput[i] == CLICKED_DECIMAL) { + value.append("."); + } else { + value.append(mInput[i]); + } + } + if (mSign == SIGN_NEGATIVE) { + + value = new StringBuilder("-" + value.toString()); + } + + return new BigDecimal(value.toString()); + } + + private void updateLeftRightButtons() { + mRight.setEnabled(canAddDecimal()); + } + + /** Enable/disable the "Set" button */ + private void enableSetButton() { + if (mSetButton == null) { + return; + } + + // Nothing entered - disable + if (mInputPointer == -1) { + mSetButton.setEnabled(false); + return; + } + + // If the user entered 1 digits or more + 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 number as currently inputted by the user + * + * @return an String representation of the number with no decimal + */ + public BigInteger getNumber() { + BigDecimal bigDecimal = getEnteredNumber().setScale(0, BigDecimal.ROUND_FLOOR); + return bigDecimal.toBigIntegerExact(); + } + + /** + * Returns the decimal following the number + * + * @return a double representation of the decimal value + */ + public double getDecimal() { + return getEnteredNumber().remainder(BigDecimal.ONE).doubleValue(); + } + + /** + * Returns whether the number is positive or negative + * + * @return true or false whether the number is positive or negative + */ + public boolean getIsNegative() { + return mSign == SIGN_NEGATIVE; + } + + @Override + public Parcelable onSaveInstanceState() { + final Parcelable parcel = super.onSaveInstanceState(); + final SavedState state = new SavedState(parcel); + state.mInput = mInput; + state.mSign = mSign; + 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; + } + mSign = savedState.mSign; + updateKeypad(); + } + + public void setNumber(Integer integerPart, Double decimalPart, Integer mCurrentSign) { + if (mCurrentSign != null) { + mSign = mCurrentSign; + } else { + mSign = SIGN_POSITIVE; + } + + if (decimalPart != null) { + String decimalString = doubleToString(decimalPart); + // remove "0." from the string + readAndRightDigits(TextUtils.substring(decimalString, 2, decimalString.length())); + mInputPointer++; + mInput[mInputPointer] = CLICKED_DECIMAL; + } + + if (integerPart != null) { + readAndRightDigits(String.valueOf(integerPart)); + } + updateKeypad(); + } + + private void readAndRightDigits(String digitsToRead) { + for (int i = digitsToRead.length() - 1; i >= 0; i--) { + mInputPointer++; + mInput[mInputPointer] = digitsToRead.charAt(i) - '0'; + } + } + + /** + * Method used to format double and avoid scientific notation x.xE-x (ex: 4.0E-4) + * + * @param value double value to format + * @return string representation of double value + */ + private String doubleToString(double value) { + // Use decimal format to avoid + DecimalFormat format = new DecimalFormat("0.0"); + format.setMaximumFractionDigits(Integer.MAX_VALUE); + return format.format(value); + } + + private static class SavedState extends BaseSavedState { + + int mInputPointer; + int[] mInput; + int mSign; + + public SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + mInputPointer = in.readInt(); + int size = in.readInt(); + if (size > 0) { + mInput = new int[size]; + in.readIntArray(mInput); + } + mSign = in.readInt(); } @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; - } - mSign = savedState.mSign; - updateKeypad(); - } - - public void setNumber(Integer integerPart, Double decimalPart, Integer mCurrentSign) { - if (mCurrentSign != null) { - mSign = mCurrentSign; - } else { - mSign = SIGN_POSITIVE; - } - - if (decimalPart != null) { - String decimalString = doubleToString(decimalPart); - // remove "0." from the string - readAndRightDigits(TextUtils.substring(decimalString, 2, decimalString.length())); - mInputPointer++; - mInput[mInputPointer] = CLICKED_DECIMAL; - } - - if (integerPart != null) { - readAndRightDigits(String.valueOf(integerPart)); - } - updateKeypad(); - } - - private void readAndRightDigits(String digitsToRead) { - for (int i = digitsToRead.length() - 1; i >= 0; i--) { - mInputPointer++; - mInput[mInputPointer] = digitsToRead.charAt(i) - '0'; - } - } - - /** - * Method used to format double and avoid scientific notation x.xE-x (ex: 4.0E-4) - * - * @param value double value to format - * @return string representation of double value - */ - private String doubleToString(double value) { - // Use decimal format to avoid - DecimalFormat format = new DecimalFormat("0.0"); - format.setMaximumFractionDigits(Integer.MAX_VALUE); - return format.format(value); - } - - - private static class SavedState extends BaseSavedState { - - int mInputPointer; - int[] mInput; - int mSign; - - public SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - mInputPointer = in.readInt(); - int size = in.readInt(); - if (size > 0) { - mInput = new int[size]; - in.readIntArray(mInput); - } - mSign = in.readInt(); - } - - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(mInputPointer); - if (mInput != null) { - dest.writeInt(mInput.length); - dest.writeIntArray(mInput); - } else { - dest.writeInt(0); - } - dest.writeInt(mSign); - } - - public static final Creator CREATOR - = new Creator() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mInputPointer); + if (mInput != null) { + dest.writeInt(mInput.length); + dest.writeIntArray(mInput); + } else { + dest.writeInt(0); + } + dest.writeInt(mSign); + } + + public static final Creator CREATOR = + new Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } }; - } + } } diff --git a/library/src/main/java/com/codetroopers/betterpickers/recurrencepicker/EventRecurrenceFormatter.java b/library/src/main/java/com/codetroopers/betterpickers/recurrencepicker/EventRecurrenceFormatter.java index 3e08d041..f4aae706 100644 --- a/library/src/main/java/com/codetroopers/betterpickers/recurrencepicker/EventRecurrenceFormatter.java +++ b/library/src/main/java/com/codetroopers/betterpickers/recurrencepicker/EventRecurrenceFormatter.java @@ -21,159 +21,162 @@ import android.text.format.DateUtils; import android.text.format.Time; import android.util.TimeFormatException; - import com.codetroopers.betterpickers.R; - import java.util.Calendar; public class EventRecurrenceFormatter { - private static int[] mMonthRepeatByDayOfWeekIds; - private static String[][] mMonthRepeatByDayOfWeekStrs; - - public static String getRepeatString(Context context, Resources r, EventRecurrence recurrence, boolean includeEndString) { - String endString = ""; - if (includeEndString) { - StringBuilder sb = new StringBuilder(); - if (recurrence.until != null) { - try { - Time time = new Time(); - time.parse(recurrence.until); - final String dateStr = DateUtils.formatDateTime(context, time.toMillis(false), DateUtils.FORMAT_NUMERIC_DATE); - sb.append(r.getString(R.string.endByDate, dateStr)); - } catch (TimeFormatException e) { - } - } + private static int[] mMonthRepeatByDayOfWeekIds; + private static String[][] mMonthRepeatByDayOfWeekStrs; + + public static String getRepeatString( + Context context, Resources r, EventRecurrence recurrence, boolean includeEndString) { + String endString = ""; + if (includeEndString) { + StringBuilder sb = new StringBuilder(); + if (recurrence.until != null) { + try { + Time time = new Time(); + time.parse(recurrence.until); + final String dateStr = + DateUtils.formatDateTime( + context, time.toMillis(false), DateUtils.FORMAT_NUMERIC_DATE); + sb.append(r.getString(R.string.endByDate, dateStr)); + } catch (TimeFormatException e) { + e.printStackTrace(); + } + } - if (recurrence.count > 0) { - sb.append(r.getQuantityString(R.plurals.endByCount, recurrence.count, - recurrence.count)); + if (recurrence.count > 0) { + sb.append(r.getQuantityString(R.plurals.endByCount, recurrence.count, recurrence.count)); + } + endString = sb.toString(); + } + + // TODO Implement "Until" portion of string, as well as custom settings + int interval = recurrence.interval <= 1 ? 1 : recurrence.interval; + switch (recurrence.freq) { + case EventRecurrence.HOURLY: + return r.getQuantityString(R.plurals.hourly, interval, interval) + endString; + case EventRecurrence.DAILY: + return r.getQuantityString(R.plurals.daily, interval, interval) + endString; + case EventRecurrence.WEEKLY: + { + if (recurrence.repeatsOnEveryWeekDay()) { + return r.getString(R.string.every_weekday) + endString; + } else { + String string; + + int dayOfWeekLength = DateUtils.LENGTH_MEDIUM; + if (recurrence.bydayCount == 1) { + dayOfWeekLength = DateUtils.LENGTH_LONG; } - endString = sb.toString(); - } - // TODO Implement "Until" portion of string, as well as custom settings - int interval = recurrence.interval <= 1 ? 1 : recurrence.interval; - switch (recurrence.freq) { - case EventRecurrence.HOURLY: - return r.getQuantityString(R.plurals.hourly, interval, interval) + endString; - case EventRecurrence.DAILY: - return r.getQuantityString(R.plurals.daily, interval, interval) + endString; - case EventRecurrence.WEEKLY: { - if (recurrence.repeatsOnEveryWeekDay()) { - return r.getString(R.string.every_weekday) + endString; - } else { - String string; - - int dayOfWeekLength = DateUtils.LENGTH_MEDIUM; - if (recurrence.bydayCount == 1) { - dayOfWeekLength = DateUtils.LENGTH_LONG; - } - - StringBuilder days = new StringBuilder(); - - // Do one less iteration in the loop so the last element is added out of the - // loop. This is done so the comma is not placed after the last item. - - if (recurrence.bydayCount > 0) { - int count = recurrence.bydayCount - 1; - for (int i = 0; i < count; i++) { - days.append(dayToString(recurrence.byday[i], dayOfWeekLength)); - days.append(", "); - } - days.append(dayToString(recurrence.byday[count], dayOfWeekLength)); - - string = days.toString(); - } else { - // There is no "BYDAY" specifier, so use the day of the - // first event. For this to work, the setStartDate() - // method must have been used by the caller to set the - // date of the first event in the recurrence. - if (recurrence.startDate == null) { - return null; - } - - int day = EventRecurrence.timeDay2Day(recurrence.startDate.weekDay); - string = dayToString(day, DateUtils.LENGTH_LONG); - } - return r.getQuantityString(R.plurals.weekly, interval, interval, string) + endString; - } + StringBuilder days = new StringBuilder(); + + // Do one less iteration in the loop so the last element is added out of the + // loop. This is done so the comma is not placed after the last item. + + if (recurrence.bydayCount > 0) { + int count = recurrence.bydayCount - 1; + for (int i = 0; i < count; i++) { + days.append(dayToString(recurrence.byday[i], dayOfWeekLength)); + days.append(", "); + } + days.append(dayToString(recurrence.byday[count], dayOfWeekLength)); + + string = days.toString(); + } else { + // There is no "BYDAY" specifier, so use the day of the + // first event. For this to work, the setStartDate() + // method must have been used by the caller to set the + // date of the first event in the recurrence. + if (recurrence.startDate == null) { + return null; + } + + int day = EventRecurrence.timeDay2Day(recurrence.startDate.weekDay); + string = dayToString(day, DateUtils.LENGTH_LONG); } - case EventRecurrence.MONTHLY: { - String details = ""; - if (recurrence.byday != null) { - int weekday = EventRecurrence.day2CalendarDay(recurrence.byday[0]) - 1; - // Cache this stuff so we won't have to redo work again later. - cacheMonthRepeatStrings(r, weekday); - - int dayNumber = recurrence.bydayNum[0]; - if(dayNumber == RecurrencePickerDialogFragment.LAST_NTH_DAY_OF_WEEK){ - dayNumber = 5; - } - details = mMonthRepeatByDayOfWeekStrs[weekday][dayNumber - 1]; - } - return r.getQuantityString(R.plurals.monthly, interval, interval, details) + endString; + return r.getQuantityString(R.plurals.weekly, interval, interval, string) + endString; + } + } + case EventRecurrence.MONTHLY: + { + String details = ""; + if (recurrence.byday != null) { + int weekday = EventRecurrence.day2CalendarDay(recurrence.byday[0]) - 1; + // Cache this stuff so we won't have to redo work again later. + cacheMonthRepeatStrings(r, weekday); + + int dayNumber = recurrence.bydayNum[0]; + if (dayNumber == RecurrencePickerDialogFragment.LAST_NTH_DAY_OF_WEEK) { + dayNumber = 5; } - case EventRecurrence.YEARLY: - return r.getQuantityString(R.plurals.yearly_plain, interval, interval, "") + endString; + details = mMonthRepeatByDayOfWeekStrs[weekday][dayNumber - 1]; + } + return r.getQuantityString(R.plurals.monthly, interval, interval, details) + endString; } - - return null; + case EventRecurrence.YEARLY: + return r.getQuantityString(R.plurals.yearly_plain, interval, interval, "") + endString; } - private static void cacheMonthRepeatStrings(Resources r, int weekday) { - if (mMonthRepeatByDayOfWeekIds == null) { - mMonthRepeatByDayOfWeekIds = new int[7]; - mMonthRepeatByDayOfWeekIds[0] = R.array.repeat_by_nth_sun; - mMonthRepeatByDayOfWeekIds[1] = R.array.repeat_by_nth_mon; - mMonthRepeatByDayOfWeekIds[2] = R.array.repeat_by_nth_tues; - mMonthRepeatByDayOfWeekIds[3] = R.array.repeat_by_nth_wed; - mMonthRepeatByDayOfWeekIds[4] = R.array.repeat_by_nth_thurs; - mMonthRepeatByDayOfWeekIds[5] = R.array.repeat_by_nth_fri; - mMonthRepeatByDayOfWeekIds[6] = R.array.repeat_by_nth_sat; - } - if (mMonthRepeatByDayOfWeekStrs == null) { - mMonthRepeatByDayOfWeekStrs = new String[7][]; - } - if (mMonthRepeatByDayOfWeekStrs[weekday] == null) { - mMonthRepeatByDayOfWeekStrs[weekday] = r.getStringArray(mMonthRepeatByDayOfWeekIds[weekday]); - } + return null; + } + + private static void cacheMonthRepeatStrings(Resources r, int weekday) { + if (mMonthRepeatByDayOfWeekIds == null) { + mMonthRepeatByDayOfWeekIds = new int[7]; + mMonthRepeatByDayOfWeekIds[0] = R.array.repeat_by_nth_sun; + mMonthRepeatByDayOfWeekIds[1] = R.array.repeat_by_nth_mon; + mMonthRepeatByDayOfWeekIds[2] = R.array.repeat_by_nth_tues; + mMonthRepeatByDayOfWeekIds[3] = R.array.repeat_by_nth_wed; + mMonthRepeatByDayOfWeekIds[4] = R.array.repeat_by_nth_thurs; + mMonthRepeatByDayOfWeekIds[5] = R.array.repeat_by_nth_fri; + mMonthRepeatByDayOfWeekIds[6] = R.array.repeat_by_nth_sat; } - - /** - * Converts day of week to a String. - * - * @param day a EventRecurrence constant - * @return day of week as a string - */ - private static String dayToString(int day, int dayOfWeekLength) { - return DateUtils.getDayOfWeekString(dayToUtilDay(day), dayOfWeekLength); + if (mMonthRepeatByDayOfWeekStrs == null) { + mMonthRepeatByDayOfWeekStrs = new String[7][]; } - - /** - * Converts EventRecurrence's day of week to DateUtil's day of week. - * - * @param day of week as an EventRecurrence value - * @return day of week as a DateUtil value. - */ - private static int dayToUtilDay(int day) { - switch (day) { - case EventRecurrence.SU: - return Calendar.SUNDAY; - case EventRecurrence.MO: - return Calendar.MONDAY; - case EventRecurrence.TU: - return Calendar.TUESDAY; - case EventRecurrence.WE: - return Calendar.WEDNESDAY; - case EventRecurrence.TH: - return Calendar.THURSDAY; - case EventRecurrence.FR: - return Calendar.FRIDAY; - case EventRecurrence.SA: - return Calendar.SATURDAY; - default: - throw new IllegalArgumentException("bad day argument: " + day); - } + if (mMonthRepeatByDayOfWeekStrs[weekday] == null) { + mMonthRepeatByDayOfWeekStrs[weekday] = r.getStringArray(mMonthRepeatByDayOfWeekIds[weekday]); + } + } + + /** + * Converts day of week to a String. + * + * @param day a EventRecurrence constant + * @return day of week as a string + */ + private static String dayToString(int day, int dayOfWeekLength) { + return DateUtils.getDayOfWeekString(dayToUtilDay(day), dayOfWeekLength); + } + + /** + * Converts EventRecurrence's day of week to DateUtil's day of week. + * + * @param day of week as an EventRecurrence value + * @return day of week as a DateUtil value. + */ + private static int dayToUtilDay(int day) { + switch (day) { + case EventRecurrence.SU: + return Calendar.SUNDAY; + case EventRecurrence.MO: + return Calendar.MONDAY; + case EventRecurrence.TU: + return Calendar.TUESDAY; + case EventRecurrence.WE: + return Calendar.WEDNESDAY; + case EventRecurrence.TH: + return Calendar.THURSDAY; + case EventRecurrence.FR: + return Calendar.FRIDAY; + case EventRecurrence.SA: + return Calendar.SATURDAY; + default: + throw new IllegalArgumentException("bad day argument: " + day); } + } } diff --git a/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneData.java b/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneData.java index 35ca325e..29375f3d 100644 --- a/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneData.java +++ b/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneData.java @@ -23,9 +23,7 @@ import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; - import com.codetroopers.betterpickers.R; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -40,493 +38,489 @@ import java.util.TimeZone; public class TimeZoneData { - private static final String TAG = "TimeZoneData"; - private static final boolean DEBUG = false; - private static final int OFFSET_ARRAY_OFFSET = 20; - - private static final String PALESTINE_COUNTRY_CODE = "PS"; - - - ArrayList mTimeZones; - LinkedHashMap> mTimeZonesByCountry; - HashSet mTimeZoneNames = new HashSet(); - - private long mTimeMillis; - private HashMap mCountryCodeToNameMap = new HashMap(); - - public String mDefaultTimeZoneId; - public static boolean is24HourFormat; - private TimeZoneInfo mDefaultTimeZoneInfo; - private String mAlternateDefaultTimeZoneId; - private String mDefaultTimeZoneCountry; - private HashMap mTimeZonesById; - private boolean[] mHasTimeZonesInHrOffset = new boolean[40]; - SparseArray> mTimeZonesByOffsets; - private Context mContext; - private String mPalestineDisplayName; - - public TimeZoneData(Context context, String defaultTimeZoneId, long timeMillis) { - mContext = context; - is24HourFormat = TimeZoneInfo.is24HourFormat = DateFormat.is24HourFormat(context); - mDefaultTimeZoneId = mAlternateDefaultTimeZoneId = defaultTimeZoneId; - long now = System.currentTimeMillis(); - - if (timeMillis == 0) { - mTimeMillis = now; - } else { - mTimeMillis = timeMillis; - } - - mPalestineDisplayName = context.getResources().getString(R.string.palestine_display_name); - - loadTzs(context); - - Log.i(TAG, "Time to load time zones (ms): " + (System.currentTimeMillis() - now)); - - // now = System.currentTimeMillis(); - // printTz(); - // Log.i(TAG, "Time to print time zones (ms): " + - // (System.currentTimeMillis() - now)); + private static final String TAG = "TimeZoneData"; + private static final boolean DEBUG = false; + private static final int OFFSET_ARRAY_OFFSET = 20; + + private static final String PALESTINE_COUNTRY_CODE = "PS"; + + ArrayList mTimeZones; + LinkedHashMap> mTimeZonesByCountry; + HashSet mTimeZoneNames = new HashSet(); + + private long mTimeMillis; + private HashMap mCountryCodeToNameMap = new HashMap(); + + public String mDefaultTimeZoneId; + public static boolean is24HourFormat; + private TimeZoneInfo mDefaultTimeZoneInfo; + private String mAlternateDefaultTimeZoneId; + private String mDefaultTimeZoneCountry; + private HashMap mTimeZonesById; + private boolean[] mHasTimeZonesInHrOffset = new boolean[40]; + SparseArray> mTimeZonesByOffsets; + private Context mContext; + private String mPalestineDisplayName; + + public TimeZoneData(Context context, String defaultTimeZoneId, long timeMillis) { + mContext = context; + is24HourFormat = TimeZoneInfo.is24HourFormat = DateFormat.is24HourFormat(context); + mDefaultTimeZoneId = mAlternateDefaultTimeZoneId = defaultTimeZoneId; + long now = System.currentTimeMillis(); + + if (timeMillis == 0) { + mTimeMillis = now; + } else { + mTimeMillis = timeMillis; } - public void setTime(long timeMillis) { - mTimeMillis = timeMillis; - } + mPalestineDisplayName = context.getResources().getString(R.string.palestine_display_name); - public TimeZoneInfo get(int position) { - return mTimeZones.get(position); - } + loadTzs(context); - public int size() { - return mTimeZones.size(); - } + Log.i(TAG, "Time to load time zones (ms): " + (System.currentTimeMillis() - now)); - public int getDefaultTimeZoneIndex() { - return mTimeZones.indexOf(mDefaultTimeZoneInfo); - } - - // TODO speed this up - public int findIndexByTimeZoneIdSlow(String timeZoneId) { - int idx = 0; - for (TimeZoneInfo tzi : mTimeZones) { - if (timeZoneId.equals(tzi.mTzId)) { - return idx; - } - idx++; - } - return -1; - } + // now = System.currentTimeMillis(); + // printTz(); + // Log.i(TAG, "Time to print time zones (ms): " + + // (System.currentTimeMillis() - now)); + } - void loadTzs(Context context) { - mTimeZones = new ArrayList(); - HashSet processedTimeZones = loadTzsInZoneTab(context); - String[] tzIds = TimeZone.getAvailableIDs(); + public void setTime(long timeMillis) { + mTimeMillis = timeMillis; + } - if (DEBUG) { - Log.e(TAG, "Available time zones: " + tzIds.length); - } + public TimeZoneInfo get(int position) { + return mTimeZones.get(position); + } - for (String tzId : tzIds) { - if (processedTimeZones.contains(tzId)) { - continue; - } + public int size() { + return mTimeZones.size(); + } - /* - * Dropping non-GMT tzs without a country code. They are not really - * needed and they are dups but missing proper country codes. e.g. - * WET CET MST7MDT PST8PDT Asia/Khandyga Asia/Ust-Nera EST - */ - if (!tzId.startsWith("Etc/GMT")) { - continue; - } + public int getDefaultTimeZoneIndex() { + return mTimeZones.indexOf(mDefaultTimeZoneInfo); + } - final TimeZone tz = TimeZone.getTimeZone(tzId); - if (tz == null) { - Log.e(TAG, "Timezone not found: " + tzId); - continue; - } - - TimeZoneInfo tzInfo = new TimeZoneInfo(tz, null); + // TODO speed this up + public int findIndexByTimeZoneIdSlow(String timeZoneId) { + int idx = 0; + for (TimeZoneInfo tzi : mTimeZones) { + if (timeZoneId.equals(tzi.mTzId)) { + return idx; + } + idx++; + } + return -1; + } - if (getIdenticalTimeZoneInTheCountry(tzInfo) == -1) { - if (DEBUG) { - Log.e(TAG, "# Adding time zone from getAvailId: " + tzInfo.toString()); - } - mTimeZones.add(tzInfo); - } else { - if (DEBUG) { - Log.e(TAG, - "# Dropping identical time zone from getAvailId: " + tzInfo.toString()); - } - continue; - } - // - // TODO check for dups - // checkForNameDups(tz, tzInfo.mCountry, false /* dls */, - // TimeZone.SHORT, groupIdx, !found); - // checkForNameDups(tz, tzInfo.mCountry, false /* dls */, - // TimeZone.LONG, groupIdx, !found); - // if (tz.useDaylightTime()) { - // checkForNameDups(tz, tzInfo.mCountry, true /* dls */, - // TimeZone.SHORT, groupIdx, - // !found); - // checkForNameDups(tz, tzInfo.mCountry, true /* dls */, - // TimeZone.LONG, groupIdx, - // !found); - // } - } + void loadTzs(Context context) { + mTimeZones = new ArrayList(); + HashSet processedTimeZones = loadTzsInZoneTab(context); + String[] tzIds = TimeZone.getAvailableIDs(); - // Don't change the order of mTimeZones after this sort - Collections.sort(mTimeZones); + if (DEBUG) { + Log.e(TAG, "Available time zones: " + tzIds.length); + } - mTimeZonesByCountry = new LinkedHashMap>(); - mTimeZonesByOffsets = new SparseArray>(mHasTimeZonesInHrOffset.length); - mTimeZonesById = new HashMap(mTimeZones.size()); - for (TimeZoneInfo tz : mTimeZones) { - // ///////////////////// - // Lookup map for id -> tz - mTimeZonesById.put(tz.mTzId, tz); + for (String tzId : tzIds) { + if (processedTimeZones.contains(tzId)) { + continue; + } + + /* + * Dropping non-GMT tzs without a country code. They are not really + * needed and they are dups but missing proper country codes. e.g. + * WET CET MST7MDT PST8PDT Asia/Khandyga Asia/Ust-Nera EST + */ + if (!tzId.startsWith("Etc/GMT")) { + continue; + } + + final TimeZone tz = TimeZone.getTimeZone(tzId); + if (tz == null) { + Log.e(TAG, "Timezone not found: " + tzId); + continue; + } + + TimeZoneInfo tzInfo = new TimeZoneInfo(tz, null); + + if (getIdenticalTimeZoneInTheCountry(tzInfo) == -1) { + if (DEBUG) { + Log.e(TAG, "# Adding time zone from getAvailId: " + tzInfo.toString()); } - populateDisplayNameOverrides(mContext.getResources()); - - Date date = new Date(mTimeMillis); - Locale defaultLocal = Locale.getDefault(); - - int idx = 0; - for (TimeZoneInfo tz : mTimeZones) { - // ///////////////////// - // Populate display name - if (tz.mDisplayName == null) { - tz.mDisplayName = tz.mTz.getDisplayName(tz.mTz.inDaylightTime(date), - TimeZone.LONG, defaultLocal); - } - - // ///////////////////// - // Grouping tz's by country for search by country - ArrayList group = mTimeZonesByCountry.get(tz.mCountry); - if (group == null) { - group = new ArrayList(); - mTimeZonesByCountry.put(tz.mCountry, group); - } - - group.add(idx); - - // ///////////////////// - // Grouping tz's by GMT offsets - indexByOffsets(idx, tz); - - // Skip all the GMT+xx:xx style display names from search - if (!tz.mDisplayName.endsWith(":00")) { - mTimeZoneNames.add(tz.mDisplayName); - } else if (DEBUG) { - Log.e(TAG, "# Hiding from pretty name search: " + - tz.mDisplayName); - } - - idx++; + mTimeZones.add(tzInfo); + } else { + if (DEBUG) { + Log.e(TAG, "# Dropping identical time zone from getAvailId: " + tzInfo.toString()); } - - // printTimeZones(); + continue; + } + // + // TODO check for dups + // checkForNameDups(tz, tzInfo.mCountry, false /* dls */, + // TimeZone.SHORT, groupIdx, !found); + // checkForNameDups(tz, tzInfo.mCountry, false /* dls */, + // TimeZone.LONG, groupIdx, !found); + // if (tz.useDaylightTime()) { + // checkForNameDups(tz, tzInfo.mCountry, true /* dls */, + // TimeZone.SHORT, groupIdx, + // !found); + // checkForNameDups(tz, tzInfo.mCountry, true /* dls */, + // TimeZone.LONG, groupIdx, + // !found); + // } } - private void printTimeZones() { - TimeZoneInfo last = null; - boolean first = true; - for (TimeZoneInfo tz : mTimeZones) { - // All - if (false) { - Log.e("ALL", tz.toString()); - } + // Don't change the order of mTimeZones after this sort + Collections.sort(mTimeZones); - // GMT - if (true) { - String name = tz.mTz.getDisplayName(); - if (name.startsWith("GMT") && !tz.mTzId.startsWith("Etc/GMT")) { - Log.e("GMT", tz.toString()); - } - } - - // Dups - if (true && last != null) { - if (last.compareTo(tz) == 0) { - if (first) { - Log.e("SAME", last.toString()); - first = false; - } - Log.e("SAME", tz.toString()); - } else { - first = true; - } - } - last = tz; - } - Log.e(TAG, "Total number of tz's = " + mTimeZones.size()); + mTimeZonesByCountry = new LinkedHashMap>(); + mTimeZonesByOffsets = new SparseArray>(mHasTimeZonesInHrOffset.length); + mTimeZonesById = new HashMap(mTimeZones.size()); + for (TimeZoneInfo tz : mTimeZones) { + // ///////////////////// + // Lookup map for id -> tz + mTimeZonesById.put(tz.mTzId, tz); + } + populateDisplayNameOverrides(mContext.getResources()); + + Date date = new Date(mTimeMillis); + Locale defaultLocal = Locale.getDefault(); + + int idx = 0; + for (TimeZoneInfo tz : mTimeZones) { + // ///////////////////// + // Populate display name + if (tz.mDisplayName == null) { + tz.mDisplayName = + tz.mTz.getDisplayName(tz.mTz.inDaylightTime(date), TimeZone.LONG, defaultLocal); + } + + // ///////////////////// + // Grouping tz's by country for search by country + ArrayList group = mTimeZonesByCountry.get(tz.mCountry); + if (group == null) { + group = new ArrayList(); + mTimeZonesByCountry.put(tz.mCountry, group); + } + + group.add(idx); + + // ///////////////////// + // Grouping tz's by GMT offsets + indexByOffsets(idx, tz); + + // Skip all the GMT+xx:xx style display names from search + if (!tz.mDisplayName.endsWith(":00")) { + mTimeZoneNames.add(tz.mDisplayName); + } else if (DEBUG) { + Log.e(TAG, "# Hiding from pretty name search: " + tz.mDisplayName); + } + + idx++; } - private void populateDisplayNameOverrides(Resources resources) { - String[] ids = resources.getStringArray(R.array.timezone_rename_ids); - String[] labels = resources.getStringArray(R.array.timezone_rename_labels); - - int length = ids.length; - if (ids.length != labels.length) { - Log.e(TAG, "timezone_rename_ids len=" + ids.length + " timezone_rename_labels len=" - + labels.length); - length = Math.min(ids.length, labels.length); + // printTimeZones(); + } + + private void printTimeZones() { + TimeZoneInfo last = null; + boolean first = true; + for (TimeZoneInfo tz : mTimeZones) { + // All + if (false) { + Log.e("ALL", tz.toString()); + } + + // GMT + if (true) { + String name = tz.mTz.getDisplayName(); + if (name.startsWith("GMT") && !tz.mTzId.startsWith("Etc/GMT")) { + Log.e("GMT", tz.toString()); } - - for (int i = 0; i < length; i++) { - TimeZoneInfo tzi = mTimeZonesById.get(ids[i]); - if (tzi != null) { - tzi.mDisplayName = labels[i]; - } else { - Log.e(TAG, "Could not find timezone with label: " + labels[i]); - } + } + + // Dups + if (true && last != null) { + if (last.compareTo(tz) == 0) { + if (first) { + Log.e("SAME", last.toString()); + first = false; + } + Log.e("SAME", tz.toString()); + } else { + first = true; } + } + last = tz; } - - public boolean hasTimeZonesInHrOffset(int offsetHr) { - int index = OFFSET_ARRAY_OFFSET + offsetHr; - if (index >= mHasTimeZonesInHrOffset.length || index < 0) { - return false; - } - return mHasTimeZonesInHrOffset[index]; + Log.e(TAG, "Total number of tz's = " + mTimeZones.size()); + } + + private void populateDisplayNameOverrides(Resources resources) { + String[] ids = resources.getStringArray(R.array.timezone_rename_ids); + String[] labels = resources.getStringArray(R.array.timezone_rename_labels); + + int length = ids.length; + if (ids.length != labels.length) { + Log.e( + TAG, + "timezone_rename_ids len=" + ids.length + " timezone_rename_labels len=" + labels.length); + length = Math.min(ids.length, labels.length); } - private void indexByOffsets(int idx, TimeZoneInfo tzi) { - int offsetMillis = tzi.getNowOffsetMillis(); - int index = OFFSET_ARRAY_OFFSET + (int) (offsetMillis / DateUtils.HOUR_IN_MILLIS); - mHasTimeZonesInHrOffset[index] = true; + for (int i = 0; i < length; i++) { + TimeZoneInfo tzi = mTimeZonesById.get(ids[i]); + if (tzi != null) { + tzi.mDisplayName = labels[i]; + } else { + Log.e(TAG, "Could not find timezone with label: " + labels[i]); + } + } + } - ArrayList group = mTimeZonesByOffsets.get(index); - if (group == null) { - group = new ArrayList(); - mTimeZonesByOffsets.put(index, group); - } - group.add(idx); + public boolean hasTimeZonesInHrOffset(int offsetHr) { + int index = OFFSET_ARRAY_OFFSET + offsetHr; + if (index >= mHasTimeZonesInHrOffset.length || index < 0) { + return false; + } + return mHasTimeZonesInHrOffset[index]; + } + + private void indexByOffsets(int idx, TimeZoneInfo tzi) { + int offsetMillis = tzi.getNowOffsetMillis(); + int index = OFFSET_ARRAY_OFFSET + (int) (offsetMillis / DateUtils.HOUR_IN_MILLIS); + mHasTimeZonesInHrOffset[index] = true; + + ArrayList group = mTimeZonesByOffsets.get(index); + if (group == null) { + group = new ArrayList(); + mTimeZonesByOffsets.put(index, group); } + group.add(idx); + } - public ArrayList getTimeZonesByOffset(int offsetHr) { - int index = OFFSET_ARRAY_OFFSET + offsetHr; - if (index >= mHasTimeZonesInHrOffset.length || index < 0) { - return null; + public ArrayList getTimeZonesByOffset(int offsetHr) { + int index = OFFSET_ARRAY_OFFSET + offsetHr; + if (index >= mHasTimeZonesInHrOffset.length || index < 0) { + return null; + } + return mTimeZonesByOffsets.get(index); + } + + private HashSet loadTzsInZoneTab(Context context) { + HashSet processedTimeZones = new HashSet(); + AssetManager am = context.getAssets(); + InputStream is = null; + + /* + * The 'backward' file contain mappings between new and old time zone + * ids. We will explicitly ignore the old ones. + */ + try { + is = am.open("backward"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line; + + while ((line = reader.readLine()) != null) { + // Skip comment lines + if (!line.startsWith("#") && line.length() > 0) { + // 0: "Link" + // 1: New tz id + // Last: Old tz id + String[] fields = line.split("\t+"); + String newTzId = fields[1]; + String oldTzId = fields[fields.length - 1]; + + final TimeZone tz = TimeZone.getTimeZone(newTzId); + if (tz == null) { + Log.e(TAG, "Timezone not found: " + newTzId); + continue; + } + + processedTimeZones.add(oldTzId); + + if (DEBUG) { + Log.e(TAG, "# Dropping identical time zone from backward: " + oldTzId); + } + + // Remember the cooler/newer time zone id + if (mDefaultTimeZoneId != null && mDefaultTimeZoneId.equals(oldTzId)) { + mAlternateDefaultTimeZoneId = newTzId; + } } - return mTimeZonesByOffsets.get(index); + } + } catch (IOException ex) { + Log.e(TAG, "Failed to read 'backward' file."); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } } - private HashSet loadTzsInZoneTab(Context context) { - HashSet processedTimeZones = new HashSet(); - AssetManager am = context.getAssets(); - InputStream is = null; - - /* - * The 'backward' file contain mappings between new and old time zone - * ids. We will explicitly ignore the old ones. - */ - try { - is = am.open("backward"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - String line; - - while ((line = reader.readLine()) != null) { - // Skip comment lines - if (!line.startsWith("#") && line.length() > 0) { - // 0: "Link" - // 1: New tz id - // Last: Old tz id - String[] fields = line.split("\t+"); - String newTzId = fields[1]; - String oldTzId = fields[fields.length - 1]; - - final TimeZone tz = TimeZone.getTimeZone(newTzId); - if (tz == null) { - Log.e(TAG, "Timezone not found: " + newTzId); - continue; - } - - processedTimeZones.add(oldTzId); - - if (DEBUG) { - Log.e(TAG, "# Dropping identical time zone from backward: " + oldTzId); - } - - // Remember the cooler/newer time zone id - if (mDefaultTimeZoneId != null && mDefaultTimeZoneId.equals(oldTzId)) { - mAlternateDefaultTimeZoneId = newTzId; - } + /* + * zone.tab contains a list of time zones and country code. They are + * "sorted first by country, then an order within the country that (1) + * makes some geographical sense, and (2) puts the most populous zones + * first, where that does not contradict (1)." + */ + try { + String lang = Locale.getDefault().getLanguage(); + is = am.open("zone.tab"); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("#")) { // Skip comment lines + // 0: country code + // 1: coordinates + // 2: time zone id + // 3: comments + final String[] fields = line.split("\t"); + final String timeZoneId = fields[2]; + final String countryCode = fields[0]; + final TimeZone tz = TimeZone.getTimeZone(timeZoneId); + if (tz == null) { + Log.e(TAG, "Timezone not found: " + timeZoneId); + continue; + } + + /* + * Dropping non-GMT tzs without a country code. They are not + * really needed and they are dups but missing proper + * country codes. e.g. WET CET MST7MDT PST8PDT Asia/Khandyga + * Asia/Ust-Nera EST + */ + if (countryCode == null && !timeZoneId.startsWith("Etc/GMT")) { + processedTimeZones.add(timeZoneId); + continue; + } + + // Remember the mapping between the country code and display + // name + String country = mCountryCodeToNameMap.get(countryCode); + if (country == null) { + country = getCountryNames(lang, countryCode); + mCountryCodeToNameMap.put(countryCode, country); + } + + // TODO Don't like this here but need to get the country of + // the default tz. + + // Find the country of the default tz + if (mDefaultTimeZoneId != null + && mDefaultTimeZoneCountry == null + && timeZoneId.equals(mAlternateDefaultTimeZoneId)) { + mDefaultTimeZoneCountry = country; + TimeZone defaultTz = TimeZone.getTimeZone(mDefaultTimeZoneId); + if (defaultTz != null) { + mDefaultTimeZoneInfo = new TimeZoneInfo(defaultTz, country); + + int tzToOverride = getIdenticalTimeZoneInTheCountry(mDefaultTimeZoneInfo); + if (tzToOverride == -1) { + if (DEBUG) { + Log.e(TAG, "Adding default time zone: " + mDefaultTimeZoneInfo.toString()); } - } - } catch (IOException ex) { - Log.e(TAG, "Failed to read 'backward' file."); - } finally { - try { - if (is != null) { - is.close(); + mTimeZones.add(mDefaultTimeZoneInfo); + } else { + mTimeZones.add(tzToOverride, mDefaultTimeZoneInfo); + if (DEBUG) { + TimeZoneInfo tzInfoToOverride = mTimeZones.get(tzToOverride); + String tzIdToOverride = tzInfoToOverride.mTzId; + Log.e(TAG, "Replaced by default tz: " + tzInfoToOverride.toString()); + Log.e(TAG, "Adding default time zone: " + mDefaultTimeZoneInfo.toString()); } - } catch (IOException ignored) { + } } - } - - /* - * zone.tab contains a list of time zones and country code. They are - * "sorted first by country, then an order within the country that (1) - * makes some geographical sense, and (2) puts the most populous zones - * first, where that does not contradict (1)." - */ - try { - String lang = Locale.getDefault().getLanguage(); - is = am.open("zone.tab"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - String line; - while ((line = reader.readLine()) != null) { - if (!line.startsWith("#")) { // Skip comment lines - // 0: country code - // 1: coordinates - // 2: time zone id - // 3: comments - final String[] fields = line.split("\t"); - final String timeZoneId = fields[2]; - final String countryCode = fields[0]; - final TimeZone tz = TimeZone.getTimeZone(timeZoneId); - if (tz == null) { - Log.e(TAG, "Timezone not found: " + timeZoneId); - continue; - } - - /* - * Dropping non-GMT tzs without a country code. They are not - * really needed and they are dups but missing proper - * country codes. e.g. WET CET MST7MDT PST8PDT Asia/Khandyga - * Asia/Ust-Nera EST - */ - if (countryCode == null && !timeZoneId.startsWith("Etc/GMT")) { - processedTimeZones.add(timeZoneId); - continue; - } - - // Remember the mapping between the country code and display - // name - String country = mCountryCodeToNameMap.get(countryCode); - if (country == null) { - country = getCountryNames(lang, countryCode); - mCountryCodeToNameMap.put(countryCode, country); - } - - // TODO Don't like this here but need to get the country of - // the default tz. - - // Find the country of the default tz - if (mDefaultTimeZoneId != null && mDefaultTimeZoneCountry == null - && timeZoneId.equals(mAlternateDefaultTimeZoneId)) { - mDefaultTimeZoneCountry = country; - TimeZone defaultTz = TimeZone.getTimeZone(mDefaultTimeZoneId); - if (defaultTz != null) { - mDefaultTimeZoneInfo = new TimeZoneInfo(defaultTz, country); - - int tzToOverride = getIdenticalTimeZoneInTheCountry(mDefaultTimeZoneInfo); - if (tzToOverride == -1) { - if (DEBUG) { - Log.e(TAG, "Adding default time zone: " - + mDefaultTimeZoneInfo.toString()); - } - mTimeZones.add(mDefaultTimeZoneInfo); - } else { - mTimeZones.add(tzToOverride, mDefaultTimeZoneInfo); - if (DEBUG) { - TimeZoneInfo tzInfoToOverride = mTimeZones.get(tzToOverride); - String tzIdToOverride = tzInfoToOverride.mTzId; - Log.e(TAG, "Replaced by default tz: " - + tzInfoToOverride.toString()); - Log.e(TAG, "Adding default time zone: " - + mDefaultTimeZoneInfo.toString()); - } - } - } - } - - // Add to the list of time zones if the time zone is unique - // in the given country. - TimeZoneInfo timeZoneInfo = new TimeZoneInfo(tz, country); - int identicalTzIdx = getIdenticalTimeZoneInTheCountry(timeZoneInfo); - if (identicalTzIdx == -1) { - if (DEBUG) { - Log.e(TAG, "# Adding time zone: " + timeZoneId + " ## " + - tz.getDisplayName()); - } - mTimeZones.add(timeZoneInfo); - } else { - if (DEBUG) { - Log.e(TAG, "# Dropping identical time zone: " + timeZoneId + " ## " + - tz.getDisplayName()); - } - } - processedTimeZones.add(timeZoneId); - } + } + + // Add to the list of time zones if the time zone is unique + // in the given country. + TimeZoneInfo timeZoneInfo = new TimeZoneInfo(tz, country); + int identicalTzIdx = getIdenticalTimeZoneInTheCountry(timeZoneInfo); + if (identicalTzIdx == -1) { + if (DEBUG) { + Log.e(TAG, "# Adding time zone: " + timeZoneId + " ## " + tz.getDisplayName()); } - - } catch (IOException ex) { - Log.e(TAG, "Failed to read 'zone.tab'."); - } finally { - try { - if (is != null) { - is.close(); - } - } catch (IOException ignored) { + mTimeZones.add(timeZoneInfo); + } else { + if (DEBUG) { + Log.e( + TAG, + "# Dropping identical time zone: " + timeZoneId + " ## " + tz.getDisplayName()); } + } + processedTimeZones.add(timeZoneId); } - - return processedTimeZones; + } + + } catch (IOException ex) { + Log.e(TAG, "Failed to read 'zone.tab'."); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } } - private static Locale mBackupCountryLocale; - private static String[] mBackupCountryCodes; - private static String[] mBackupCountryNames; + return processedTimeZones; + } - private String getCountryNames(String lang, String countryCode) { - final Locale defaultLocale = Locale.getDefault(); - String countryDisplayName; - if (PALESTINE_COUNTRY_CODE.equalsIgnoreCase(countryCode)) { - countryDisplayName = mPalestineDisplayName; - } else { - countryDisplayName = new Locale(lang, countryCode).getDisplayCountry(defaultLocale); - } + private static Locale mBackupCountryLocale; + private static String[] mBackupCountryCodes; + private static String[] mBackupCountryNames; - if (!countryCode.equals(countryDisplayName)) { - return countryDisplayName; - } + private String getCountryNames(String lang, String countryCode) { + final Locale defaultLocale = Locale.getDefault(); + String countryDisplayName; + if (PALESTINE_COUNTRY_CODE.equalsIgnoreCase(countryCode)) { + countryDisplayName = mPalestineDisplayName; + } else { + countryDisplayName = new Locale(lang, countryCode).getDisplayCountry(defaultLocale); + } - if (mBackupCountryCodes == null || !defaultLocale.equals(mBackupCountryLocale)) { - mBackupCountryLocale = defaultLocale; - mBackupCountryCodes = mContext.getResources().getStringArray( - R.array.backup_country_codes); - mBackupCountryNames = mContext.getResources().getStringArray( - R.array.backup_country_names); - } + if (!countryCode.equals(countryDisplayName)) { + return countryDisplayName; + } - int length = Math.min(mBackupCountryCodes.length, mBackupCountryNames.length); + if (mBackupCountryCodes == null || !defaultLocale.equals(mBackupCountryLocale)) { + mBackupCountryLocale = defaultLocale; + mBackupCountryCodes = mContext.getResources().getStringArray(R.array.backup_country_codes); + mBackupCountryNames = mContext.getResources().getStringArray(R.array.backup_country_names); + } - for (int i = 0; i < length; i++) { - if (mBackupCountryCodes[i].equals(countryCode)) { - return mBackupCountryNames[i]; - } - } + int length = Math.min(mBackupCountryCodes.length, mBackupCountryNames.length); - return countryCode; + for (int i = 0; i < length; i++) { + if (mBackupCountryCodes[i].equals(countryCode)) { + return mBackupCountryNames[i]; + } } - private int getIdenticalTimeZoneInTheCountry(TimeZoneInfo timeZoneInfo) { - int idx = 0; - for (TimeZoneInfo tzi : mTimeZones) { - if (tzi.hasSameRules(timeZoneInfo)) { - if (tzi.mCountry == null) { - if (timeZoneInfo.mCountry == null) { - return idx; - } - } else if (tzi.mCountry.equals(timeZoneInfo.mCountry)) { - return idx; - } - } - ++idx; + return countryCode; + } + + private int getIdenticalTimeZoneInTheCountry(TimeZoneInfo timeZoneInfo) { + int idx = 0; + for (TimeZoneInfo tzi : mTimeZones) { + if (tzi.hasSameRules(timeZoneInfo)) { + if (tzi.mCountry == null) { + if (timeZoneInfo.mCountry == null) { + return idx; + } + } else if (tzi.mCountry.equals(timeZoneInfo.mCountry)) { + return idx; } - return -1; + } + ++idx; } + return -1; + } } diff --git a/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneInfo.java b/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneInfo.java index 7a002920..cfac6663 100644 --- a/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneInfo.java +++ b/library/src/main/java/com/codetroopers/betterpickers/timezonepicker/TimeZoneInfo.java @@ -26,7 +26,6 @@ import android.text.style.ForegroundColorSpan; import android.util.Log; import android.util.SparseArray; - import java.lang.reflect.Field; import java.text.DateFormat; import java.util.Arrays; @@ -37,331 +36,341 @@ public class TimeZoneInfo implements Comparable { - private static final int GMT_TEXT_COLOR = TimeZonePickerUtils.GMT_TEXT_COLOR; - private static final int DST_SYMBOL_COLOR = TimeZonePickerUtils.DST_SYMBOL_COLOR; - private static final char SEPARATOR = ','; - private static final String TAG = null; - public static int NUM_OF_TRANSITIONS = 6; - public static long time = System.currentTimeMillis() / 1000; - public static boolean is24HourFormat; - private static final Factory mSpannableFactory = Spannable.Factory.getInstance(); - - TimeZone mTz; - public String mTzId; - int mRawoffset; - public long[] mTransitions; // may have trailing 0's. - public String mCountry; - public int groupId; - public String mDisplayName; - private Time recycledTime = new Time(); - private static StringBuilder mSB = new StringBuilder(50); - private static Formatter mFormatter = new Formatter(mSB, Locale.getDefault()); - - public TimeZoneInfo(TimeZone tz, String country) { - mTz = tz; - mTzId = tz.getID(); - mCountry = country; - mRawoffset = tz.getRawOffset(); - - try { - mTransitions = getTransitions(tz, time); - } catch (NoSuchFieldException ignored) { - } catch (IllegalAccessException ignored) { - ignored.printStackTrace(); - } + private static final int GMT_TEXT_COLOR = TimeZonePickerUtils.GMT_TEXT_COLOR; + private static final int DST_SYMBOL_COLOR = TimeZonePickerUtils.DST_SYMBOL_COLOR; + private static final char SEPARATOR = ','; + private static final String TAG = null; + public static int NUM_OF_TRANSITIONS = 6; + public static long time = System.currentTimeMillis() / 1000; + public static boolean is24HourFormat; + private static final Factory mSpannableFactory = Spannable.Factory.getInstance(); + + TimeZone mTz; + public String mTzId; + int mRawoffset; + public long[] mTransitions; // may have trailing 0's. + public String mCountry; + public int groupId; + public String mDisplayName; + private Time recycledTime = new Time(); + private static StringBuilder mSB = new StringBuilder(50); + private static Formatter mFormatter = new Formatter(mSB, Locale.getDefault()); + + public TimeZoneInfo(TimeZone tz, String country) { + mTz = tz; + mTzId = tz.getID(); + mCountry = country; + mRawoffset = tz.getRawOffset(); + + try { + mTransitions = getTransitions(tz, time); + } catch (NoSuchFieldException ignored) { + ignored.printStackTrace(); + } catch (IllegalAccessException ignored) { + ignored.printStackTrace(); } + } - SparseArray mLocalTimeCache = new SparseArray(); - long mLocalTimeCacheReferenceTime = 0; - static private long mGmtDisplayNameUpdateTime; - static private SparseArray mGmtDisplayNameCache = - new SparseArray(); + SparseArray mLocalTimeCache = new SparseArray(); + long mLocalTimeCacheReferenceTime = 0; + private static long mGmtDisplayNameUpdateTime; + private static SparseArray mGmtDisplayNameCache = new SparseArray(); - public String getLocalTime(long referenceTime) { - recycledTime.timezone = TimeZone.getDefault().getID(); - recycledTime.set(referenceTime); + public String getLocalTime(long referenceTime) { + recycledTime.timezone = TimeZone.getDefault().getID(); + recycledTime.set(referenceTime); - int currYearDay = recycledTime.year * 366 + recycledTime.yearDay; + int currYearDay = recycledTime.year * 366 + recycledTime.yearDay; - recycledTime.timezone = mTzId; - recycledTime.set(referenceTime); + recycledTime.timezone = mTzId; + recycledTime.set(referenceTime); - String localTimeStr = null; + String localTimeStr = null; - int hourMinute = recycledTime.hour * 60 + - recycledTime.minute; + int hourMinute = recycledTime.hour * 60 + recycledTime.minute; - if (mLocalTimeCacheReferenceTime != referenceTime) { - mLocalTimeCacheReferenceTime = referenceTime; - mLocalTimeCache.clear(); - } else { - localTimeStr = mLocalTimeCache.get(hourMinute); - } + if (mLocalTimeCacheReferenceTime != referenceTime) { + mLocalTimeCacheReferenceTime = referenceTime; + mLocalTimeCache.clear(); + } else { + localTimeStr = mLocalTimeCache.get(hourMinute); + } - if (localTimeStr == null) { - String format = "%I:%M %p"; - if (currYearDay != (recycledTime.year * 366 + recycledTime.yearDay)) { - if (is24HourFormat) { - format = "%b %d %H:%M"; - } else { - format = "%b %d %I:%M %p"; - } - } else if (is24HourFormat) { - format = "%H:%M"; - } - - // format = "%Y-%m-%d %H:%M"; - localTimeStr = recycledTime.format(format); - mLocalTimeCache.put(hourMinute, localTimeStr); + if (localTimeStr == null) { + String format = "%I:%M %p"; + if (currYearDay != (recycledTime.year * 366 + recycledTime.yearDay)) { + if (is24HourFormat) { + format = "%b %d %H:%M"; + } else { + format = "%b %d %I:%M %p"; } + } else if (is24HourFormat) { + format = "%H:%M"; + } - return localTimeStr; + // format = "%Y-%m-%d %H:%M"; + localTimeStr = recycledTime.format(format); + mLocalTimeCache.put(hourMinute, localTimeStr); } - public int getLocalHr(long referenceTime) { - recycledTime.timezone = mTzId; - recycledTime.set(referenceTime); - return recycledTime.hour; + return localTimeStr; + } + + public int getLocalHr(long referenceTime) { + recycledTime.timezone = mTzId; + recycledTime.set(referenceTime); + return recycledTime.hour; + } + + public int getNowOffsetMillis() { + return mTz.getOffset(System.currentTimeMillis()); + } + + /* + * The method is synchronized because there's one mSB, which is used by + * mFormatter, per instance. If there are multiple callers for + * getGmtDisplayName, the output may be mangled. + */ + public synchronized CharSequence getGmtDisplayName(Context context) { + // TODO Note: The local time is shown in current time (current GMT + // offset) which may be different from the time specified by + // mTimeMillis + + final long nowMinute = System.currentTimeMillis() / DateUtils.MINUTE_IN_MILLIS; + final long now = nowMinute * DateUtils.MINUTE_IN_MILLIS; + final int gmtOffset = mTz.getOffset(now); + int cacheKey; + + boolean hasFutureDST = mTz.useDaylightTime(); + if (hasFutureDST) { + cacheKey = (int) (gmtOffset + 36 * DateUtils.HOUR_IN_MILLIS); + } else { + cacheKey = (int) (gmtOffset - 36 * DateUtils.HOUR_IN_MILLIS); } - public int getNowOffsetMillis() { - return mTz.getOffset(System.currentTimeMillis()); + CharSequence displayName = null; + if (mGmtDisplayNameUpdateTime != nowMinute) { + mGmtDisplayNameUpdateTime = nowMinute; + mGmtDisplayNameCache.clear(); + } else { + displayName = mGmtDisplayNameCache.get(cacheKey); } - /* - * The method is synchronized because there's one mSB, which is used by - * mFormatter, per instance. If there are multiple callers for - * getGmtDisplayName, the output may be mangled. - */ - public synchronized CharSequence getGmtDisplayName(Context context) { - // TODO Note: The local time is shown in current time (current GMT - // offset) which may be different from the time specified by - // mTimeMillis - - final long nowMinute = System.currentTimeMillis() / DateUtils.MINUTE_IN_MILLIS; - final long now = nowMinute * DateUtils.MINUTE_IN_MILLIS; - final int gmtOffset = mTz.getOffset(now); - int cacheKey; - - boolean hasFutureDST = mTz.useDaylightTime(); - if (hasFutureDST) { - cacheKey = (int) (gmtOffset + 36 * DateUtils.HOUR_IN_MILLIS); - } else { - cacheKey = (int) (gmtOffset - 36 * DateUtils.HOUR_IN_MILLIS); - } - - CharSequence displayName = null; - if (mGmtDisplayNameUpdateTime != nowMinute) { - mGmtDisplayNameUpdateTime = nowMinute; - mGmtDisplayNameCache.clear(); - } else { - displayName = mGmtDisplayNameCache.get(cacheKey); - } - - if (displayName == null) { - mSB.setLength(0); - int flags = DateUtils.FORMAT_ABBREV_ALL; - flags |= DateUtils.FORMAT_SHOW_TIME; - if (TimeZoneInfo.is24HourFormat) { - flags |= DateUtils.FORMAT_24HOUR; - } - - // mFormatter writes to mSB - DateUtils.formatDateRange(context, mFormatter, now, now, flags, mTzId); - mSB.append(" "); - int gmtStart = mSB.length(); - TimeZonePickerUtils.appendGmtOffset(mSB, gmtOffset); - int gmtEnd = mSB.length(); - - int symbolStart = 0; - int symbolEnd = 0; - if (hasFutureDST) { - mSB.append(' '); - symbolStart = mSB.length(); - mSB.append(TimeZonePickerUtils.getDstSymbol()); // Sun symbol - symbolEnd = mSB.length(); - } - - // Set the gray colors. - Spannable spannableText = mSpannableFactory.newSpannable(mSB); - spannableText.setSpan(new ForegroundColorSpan(GMT_TEXT_COLOR), - gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - if (hasFutureDST) { - spannableText.setSpan(new ForegroundColorSpan(DST_SYMBOL_COLOR), - symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - displayName = spannableText; - mGmtDisplayNameCache.put(cacheKey, displayName); - } - return displayName; + if (displayName == null) { + mSB.setLength(0); + int flags = DateUtils.FORMAT_ABBREV_ALL; + flags |= DateUtils.FORMAT_SHOW_TIME; + if (TimeZoneInfo.is24HourFormat) { + flags |= DateUtils.FORMAT_24HOUR; + } + + // mFormatter writes to mSB + DateUtils.formatDateRange(context, mFormatter, now, now, flags, mTzId); + mSB.append(" "); + int gmtStart = mSB.length(); + TimeZonePickerUtils.appendGmtOffset(mSB, gmtOffset); + int gmtEnd = mSB.length(); + + int symbolStart = 0; + int symbolEnd = 0; + if (hasFutureDST) { + mSB.append(' '); + symbolStart = mSB.length(); + mSB.append(TimeZonePickerUtils.getDstSymbol()); // Sun symbol + symbolEnd = mSB.length(); + } + + // Set the gray colors. + Spannable spannableText = mSpannableFactory.newSpannable(mSB); + spannableText.setSpan( + new ForegroundColorSpan(GMT_TEXT_COLOR), + gmtStart, + gmtEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + if (hasFutureDST) { + spannableText.setSpan( + new ForegroundColorSpan(DST_SYMBOL_COLOR), + symbolStart, + symbolEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + displayName = spannableText; + mGmtDisplayNameCache.put(cacheKey, displayName); } - - private static long[] getTransitions(TimeZone tz, long time) throws IllegalAccessException, NoSuchFieldException { - Class zoneInfoClass = tz.getClass(); - Field mTransitionsField = zoneInfoClass.getDeclaredField("mTransitions"); - mTransitionsField.setAccessible(true); - long[] objTransitions; - long[] transitions = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - objTransitions = (long[]) mTransitionsField.get(tz); - } else { - objTransitions = copyFromIntArray((int[]) mTransitionsField.get(tz)); - } - if (objTransitions.length != 0) { - transitions = new long[NUM_OF_TRANSITIONS]; - int numOfTransitions = 0; - for (int i = 0; i < objTransitions.length; ++i) { - if (objTransitions[i] < time) { - continue; - } - transitions[numOfTransitions++] = objTransitions[i]; - if (numOfTransitions == NUM_OF_TRANSITIONS) { - break; - } - } - } - return transitions; + return displayName; + } + + private static long[] getTransitions(TimeZone tz, long time) + throws IllegalAccessException, NoSuchFieldException { + Class zoneInfoClass = tz.getClass(); + Field mTransitionsField = zoneInfoClass.getDeclaredField("mTransitions"); + mTransitionsField.setAccessible(true); + long[] objTransitions; + long[] transitions = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + objTransitions = (long[]) mTransitionsField.get(tz); + } else { + objTransitions = copyFromIntArray((int[]) mTransitionsField.get(tz)); } - - public static long[] copyFromIntArray(int[] source) { - if(source == null){ - return new long[0]; + if (objTransitions.length != 0) { + transitions = new long[NUM_OF_TRANSITIONS]; + int numOfTransitions = 0; + for (int i = 0; i < objTransitions.length; ++i) { + if (objTransitions[i] < time) { + continue; } - long[] dest = new long[source.length]; - for (int i = 0; i < source.length; i++) { - dest[i] = source[i]; + transitions[numOfTransitions++] = objTransitions[i]; + if (numOfTransitions == NUM_OF_TRANSITIONS) { + break; } - return dest; + } } + return transitions; + } - public boolean hasSameRules(TimeZoneInfo tzi) { - // this.mTz.hasSameRules(tzi.mTz) - - return this.mRawoffset == tzi.mRawoffset - && Arrays.equals(this.mTransitions, tzi.mTransitions); + public static long[] copyFromIntArray(int[] source) { + if (source == null) { + return new long[0]; } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - final String country = this.mCountry; - final TimeZone tz = this.mTz; - - sb.append(mTzId); - sb.append(SEPARATOR); - sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.LONG)); - sb.append(SEPARATOR); - sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.SHORT)); - sb.append(SEPARATOR); - if (tz.useDaylightTime()) { - sb.append(tz.getDisplayName(true, TimeZone.LONG)); - sb.append(SEPARATOR); - sb.append(tz.getDisplayName(true, TimeZone.SHORT)); - } else { - sb.append(SEPARATOR); - } - sb.append(SEPARATOR); - sb.append(tz.getRawOffset() / 3600000f); - sb.append(SEPARATOR); - sb.append(tz.getDSTSavings() / 3600000f); - sb.append(SEPARATOR); - sb.append(country); - sb.append(SEPARATOR); - - // 1-1-2013 noon GMT - sb.append(getLocalTime(1357041600000L)); - sb.append(SEPARATOR); - - // 3-15-2013 noon GMT - sb.append(getLocalTime(1363348800000L)); - sb.append(SEPARATOR); - - // 7-1-2013 noon GMT - sb.append(getLocalTime(1372680000000L)); - sb.append(SEPARATOR); - - // 11-01-2013 noon GMT - sb.append(getLocalTime(1383307200000L)); - sb.append(SEPARATOR); - - // if (this.mTransitions != null && this.mTransitions.length != 0) { - // sb.append('"'); - // DateFormat df = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss Z", - // Locale.US); - // df.setTimeZone(tz); - // DateFormat weekdayFormat = new SimpleDateFormat("EEEE", Locale.US); - // weekdayFormat.setTimeZone(tz); - // Formatter f = new Formatter(sb); - // for (int i = 0; i < this.mTransitions.length; ++i) { - // if (this.mTransitions[i] < time) { - // continue; - // } - // - // String fromTime = formatTime(df, this.mTransitions[i] - 1); - // String toTime = formatTime(df, this.mTransitions[i]); - // f.format("%s -> %s (%d)", fromTime, toTime, this.mTransitions[i]); - // - // String weekday = weekdayFormat.format(new Date(1000L * - // this.mTransitions[i])); - // if (!weekday.equals("Sunday")) { - // f.format(" -- %s", weekday); - // } - // sb.append("##"); - // } - // sb.append('"'); - // } - // sb.append(SEPARATOR); - sb.append('\n'); - return sb.toString(); + long[] dest = new long[source.length]; + for (int i = 0; i < source.length; i++) { + dest[i] = source[i]; } - - private static String formatTime(DateFormat df, int s) { - long ms = s * 1000L; - return df.format(new Date(ms)); + return dest; + } + + public boolean hasSameRules(TimeZoneInfo tzi) { + // this.mTz.hasSameRules(tzi.mTz) + + return this.mRawoffset == tzi.mRawoffset && Arrays.equals(this.mTransitions, tzi.mTransitions); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + final String country = this.mCountry; + final TimeZone tz = this.mTz; + + sb.append(mTzId); + sb.append(SEPARATOR); + sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.LONG)); + sb.append(SEPARATOR); + sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.SHORT)); + sb.append(SEPARATOR); + if (tz.useDaylightTime()) { + sb.append(tz.getDisplayName(true, TimeZone.LONG)); + sb.append(SEPARATOR); + sb.append(tz.getDisplayName(true, TimeZone.SHORT)); + } else { + sb.append(SEPARATOR); + } + sb.append(SEPARATOR); + sb.append(tz.getRawOffset() / 3600000f); + sb.append(SEPARATOR); + sb.append(tz.getDSTSavings() / 3600000f); + sb.append(SEPARATOR); + sb.append(country); + sb.append(SEPARATOR); + + // 1-1-2013 noon GMT + sb.append(getLocalTime(1357041600000L)); + sb.append(SEPARATOR); + + // 3-15-2013 noon GMT + sb.append(getLocalTime(1363348800000L)); + sb.append(SEPARATOR); + + // 7-1-2013 noon GMT + sb.append(getLocalTime(1372680000000L)); + sb.append(SEPARATOR); + + // 11-01-2013 noon GMT + sb.append(getLocalTime(1383307200000L)); + sb.append(SEPARATOR); + + // if (this.mTransitions != null && this.mTransitions.length != 0) { + // sb.append('"'); + // DateFormat df = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss Z", + // Locale.US); + // df.setTimeZone(tz); + // DateFormat weekdayFormat = new SimpleDateFormat("EEEE", Locale.US); + // weekdayFormat.setTimeZone(tz); + // Formatter f = new Formatter(sb); + // for (int i = 0; i < this.mTransitions.length; ++i) { + // if (this.mTransitions[i] < time) { + // continue; + // } + // + // String fromTime = formatTime(df, this.mTransitions[i] - 1); + // String toTime = formatTime(df, this.mTransitions[i]); + // f.format("%s -> %s (%d)", fromTime, toTime, this.mTransitions[i]); + // + // String weekday = weekdayFormat.format(new Date(1000L * + // this.mTransitions[i])); + // if (!weekday.equals("Sunday")) { + // f.format(" -- %s", weekday); + // } + // sb.append("##"); + // } + // sb.append('"'); + // } + // sb.append(SEPARATOR); + sb.append('\n'); + return sb.toString(); + } + + private static String formatTime(DateFormat df, int s) { + long ms = s * 1000L; + return df.format(new Date(ms)); + } + + /* + * Returns a negative integer if this instance is less than the other; a + * positive integer if this instance is greater than the other; 0 if this + * instance has the same order as the other. + */ + @Override + public int compareTo(@NonNull TimeZoneInfo other) { + if (this.getNowOffsetMillis() != other.getNowOffsetMillis()) { + return (other.getNowOffsetMillis() < this.getNowOffsetMillis()) ? -1 : 1; } - /* - * Returns a negative integer if this instance is less than the other; a - * positive integer if this instance is greater than the other; 0 if this - * instance has the same order as the other. - */ - @Override - public int compareTo(@NonNull TimeZoneInfo other) { - if (this.getNowOffsetMillis() != other.getNowOffsetMillis()) { - return (other.getNowOffsetMillis() < this.getNowOffsetMillis()) ? -1 : 1; - } - - // By country - if (this.mCountry == null) { - if (other.mCountry != null) { - return 1; - } - } - - if (other.mCountry == null) { - return -1; - } else { - int diff = this.mCountry.compareTo(other.mCountry); - - if (diff != 0) { - return diff; - } - } + // By country + if (this.mCountry == null) { + if (other.mCountry != null) { + return 1; + } + } - if (Arrays.equals(this.mTransitions, other.mTransitions)) { - Log.e(TAG, "Not expected to be comparing tz with the same country, same offset," + - " same dst, same transitions:\n" + this.toString() + "\n" + other.toString()); - } + if (other.mCountry == null) { + return -1; + } else { + int diff = this.mCountry.compareTo(other.mCountry); - // Finally diff by display name - if (mDisplayName != null && other.mDisplayName != null) { - return this.mDisplayName.compareTo(other.mDisplayName); - } + if (diff != 0) { + return diff; + } + } - return this.mTz.getDisplayName(Locale.getDefault()).compareTo( - other.mTz.getDisplayName(Locale.getDefault())); + if (Arrays.equals(this.mTransitions, other.mTransitions)) { + Log.e( + TAG, + "Not expected to be comparing tz with the same country, same offset," + + " same dst, same transitions:\n" + + this.toString() + + "\n" + + other.toString()); + } + // Finally diff by display name + if (mDisplayName != null && other.mDisplayName != null) { + return this.mDisplayName.compareTo(other.mDisplayName); } + + return this.mTz + .getDisplayName(Locale.getDefault()) + .compareTo(other.mTz.getDisplayName(Locale.getDefault())); + } } diff --git a/sample/src/main/java/com/codetroopers/betterpickers/sample/ListSamples.java b/sample/src/main/java/com/codetroopers/betterpickers/sample/ListSamples.java index 1d1d17b0..75e9d877 100644 --- a/sample/src/main/java/com/codetroopers/betterpickers/sample/ListSamples.java +++ b/sample/src/main/java/com/codetroopers/betterpickers/sample/ListSamples.java @@ -10,7 +10,6 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.SimpleAdapter; - import java.text.Collator; import java.util.ArrayList; import java.util.Collections; @@ -21,135 +20,143 @@ public class ListSamples extends AppCompatActivity { - public static final String INTENT_PATH = "com.doomonafireball.betterpickers.sample.Path"; - public static final String INTENT_SAMPLE = "com.doomonafireball.betterpickers.sample.SAMPLE"; + public static final String INTENT_PATH = "com.doomonafireball.betterpickers.sample.Path"; + public static final String INTENT_SAMPLE = "com.doomonafireball.betterpickers.sample.SAMPLE"; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.list); + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.list); - Intent intent = getIntent(); - String path = intent.getStringExtra(INTENT_PATH); + Intent intent = getIntent(); + String path = intent.getStringExtra(INTENT_PATH); - if (path == null) { - path = ""; - } else { - getSupportActionBar().setTitle(path); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } + if (path == null) { + path = ""; + } else { + getSupportActionBar().setTitle(path); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } - final ListView list = (ListView) findViewById(R.id.list); - list.setAdapter(new SimpleAdapter(this, getData(path), - android.R.layout.simple_list_item_1, new String[]{"title"}, - new int[]{android.R.id.text1})); - list.setTextFilterEnabled(true); - list.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Map map = (Map) list.getItemAtPosition(position); - - Intent intent = (Intent) map.get("intent"); - startActivity(intent); - } + final ListView list = (ListView) findViewById(R.id.list); + list.setAdapter( + new SimpleAdapter( + this, + getData(path), + android.R.layout.simple_list_item_1, + new String[] {"title"}, + new int[] {android.R.id.text1})); + list.setTextFilterEnabled(true); + list.setOnItemClickListener( + new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Map map = (Map) list.getItemAtPosition(position); + + Intent intent = (Intent) map.get("intent"); + startActivity(intent); + } }); - } + } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); return true; } + return true; + } - protected List> getData(String prefix) { - List> myData = new ArrayList>(); + protected List> getData(String prefix) { + List> myData = new ArrayList>(); - Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(INTENT_SAMPLE); + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(INTENT_SAMPLE); - PackageManager pm = getPackageManager(); - List list = pm.queryIntentActivities(mainIntent, 0); + PackageManager pm = getPackageManager(); + List list = pm.queryIntentActivities(mainIntent, 0); - if (null == list) { - return myData; - } + if (null == list) { + return myData; + } - String[] prefixPath; - String prefixWithSlash = prefix; + String[] prefixPath; + String prefixWithSlash = prefix; - if (prefix.equals("")) { - prefixPath = null; - } else { - prefixPath = prefix.split("/"); - prefixWithSlash = prefix + "/"; - } + if (prefix.equals("")) { + prefixPath = null; + } else { + prefixPath = prefix.split("/"); + prefixWithSlash = prefix + "/"; + } - int len = list.size(); + int len = list.size(); - Map entries = new HashMap(); + Map entries = new HashMap(); - for (int i = 0; i < len; i++) { - ResolveInfo info = list.get(i); - CharSequence labelSeq = info.loadLabel(pm); - String label = labelSeq != null - ? labelSeq.toString() - : info.activityInfo.name; + for (int i = 0; i < len; i++) { + ResolveInfo info = list.get(i); + CharSequence labelSeq = info.loadLabel(pm); + String label = labelSeq != null ? labelSeq.toString() : info.activityInfo.name; - if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { + if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) { - String[] labelPath = label.split("/"); + String[] labelPath = label.split("/"); - String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; + String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length]; - if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { - addItem(myData, nextLabel, activityIntent( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name)); - } else { - if (entries.get(nextLabel) == null) { - addItem(myData, nextLabel, - browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); - entries.put(nextLabel, true); - } - } - } + if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) { + addItem( + myData, + nextLabel, + activityIntent( + info.activityInfo.applicationInfo.packageName, info.activityInfo.name)); + } else { + if (entries.get(nextLabel) == null) { + addItem( + myData, + nextLabel, + browseIntent(prefix.equals("") ? nextLabel : prefix + "/" + nextLabel)); + entries.put(nextLabel, true); + } } - - Collections.sort(myData, NAME_COMPARATOR); - - return myData; + } } - private final static Comparator> NAME_COMPARATOR = - new Comparator>() { - private final Collator collator = Collator.getInstance(); + Collections.sort(myData, NAME_COMPARATOR); - public int compare(Map map1, Map map2) { - return collator.compare(map1.get("title"), map2.get("title")); - } - }; + return myData; + } - protected Intent activityIntent(String pkg, String componentName) { - Intent result = new Intent(); - result.setClassName(pkg, componentName); - return result; - } + private static final Comparator> NAME_COMPARATOR = + new Comparator>() { + private final Collator collator = Collator.getInstance(); - protected Intent browseIntent(String path) { - Intent result = new Intent(); - result.setClass(this, ListSamples.class); - result.putExtra(INTENT_PATH, path); - return result; - } - - protected void addItem(List> data, String name, Intent intent) { - Map temp = new HashMap(); - temp.put("title", name); - temp.put("intent", intent); - data.add(temp); - } + @Override + public int compare(Map map1, Map map2) { + return collator.compare(map1.get("title"), map2.get("title")); + } + }; + + protected Intent activityIntent(String pkg, String componentName) { + Intent result = new Intent(); + result.setClassName(pkg, componentName); + return result; + } + + protected Intent browseIntent(String path) { + Intent result = new Intent(); + result.setClass(this, ListSamples.class); + result.putExtra(INTENT_PATH, path); + return result; + } + + protected void addItem(List> data, String name, Intent intent) { + Map temp = new HashMap(); + temp.put("title", name); + temp.put("intent", intent); + data.add(temp); + } }