package jp.webpay.android.token.ui.field; import android.content.Context; import android.text.InputType; import android.util.AttributeSet; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import jp.webpay.android.token.R; import jp.webpay.android.token.model.CardType; import jp.webpay.android.token.model.RawCard; import jp.webpay.android.token.validator.CardNumberValidator; public class NumberField extends MultiColumnCardField { public static final String SEPARATOR = " "; private String mValidNumber; private OnCardTypeChangeListener mOnCardTypeChangeListener; private CardType mCurrentCardType; private List<CardType> mCardTypesSupported; public NumberField(Context context) { super(context, SEPARATOR); initialize(); } public NumberField(Context context, AttributeSet attrs) { super(context, attrs, SEPARATOR); initialize(); } public NumberField(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle, SEPARATOR); initialize(); } private void initialize() { setInputType(InputType.TYPE_CLASS_NUMBER); setHint(R.string.field_number_hint); } @Override protected boolean validateCurrentValue() { String value = getText().toString().replace(SEPARATOR, ""); if (CardNumberValidator.isValid(value, mCardTypesSupported)) { mValidNumber = value; return true; } else { mValidNumber = null; return false; } } @Override public void updateCard(RawCard card) { card.number(mValidNumber); } @Override protected String formatVisibleText(String current) { CardType cardType = expectCardType(current); List<Integer> separatorIndex; if (cardType == CardType.AMERICAN_EXPRESS || cardType == CardType.DINERS_CLUB) { separatorIndex = Arrays.asList(4, 10); } else { separatorIndex = Arrays.asList(4, 8, 12); } StringBuilder builder = new StringBuilder(); int validChars = 0; for (int i = 0; i < current.length(); i++) { char ch = current.charAt(i); if (ch >= '0' && ch <= '9') { builder.append(ch); validChars += 1; if (validChars >= 16) break; if (separatorIndex.contains(validChars)) { builder.append(SEPARATOR); } } } String visibleText = builder.toString(); notifyCardTypeChange(visibleText); return visibleText; } private CardType expectCardType(String number) { if (Pattern.matches("4[0-9].*", number)) { return CardType.VISA; } if (Pattern.matches("3[47].*", number)) { return CardType.AMERICAN_EXPRESS; } if (Pattern.matches("5[1-5].*", number)) { return CardType.MASTERCARD; } if (Pattern.matches("3[0689].*", number)) { return CardType.DINERS_CLUB; } if (Pattern.matches("35.*", number)) { return CardType.JCB; } return null; } private void notifyCardTypeChange(String number) { CardType cardType = expectCardType(number); boolean isSame = mCurrentCardType == null ? cardType == null : mCurrentCardType.equals(cardType); if (!isSame) { mCurrentCardType = cardType; if (mOnCardTypeChangeListener != null) { mOnCardTypeChangeListener.onCardTypeChange(cardType); } } } public String getValidNumber() { return mValidNumber; } public void setOnCardTypeChangeListener(OnCardTypeChangeListener mListener) { this.mOnCardTypeChangeListener = mListener; } public void setCardTypesSupported(List<CardType> cardTypesSupported) { this.mCardTypesSupported = cardTypesSupported; } public static interface OnCardTypeChangeListener { public void onCardTypeChange(CardType cardType); } }