Search code examples
androidandroid-edittextdate-format

How to allow mm/yyyy format in edittext for entering credit/debit card details


I have an activity where credit/debit card details is added. Currently my code allows to add date and month in MM/YY format. But i need year in 4 digits format MM/YYYY. Adding the '/' divider is set after 2nd digit. So if i increase my total digits count, i get the divider after every 2nd digit. How to fix this?

Carddetails.java:

public class CardDetailsActivity extends AppCompatActivity implements TextWatcher {
    private static final int CARD_NUMBER_TOTAL_SYMBOLS = 19; // size of pattern 0000-0000-0000-0000
    private static final int CARD_NUMBER_TOTAL_DIGITS = 16; // max numbers of digits in pattern: 0000 x walknew2
    private static final int CARD_NUMBER_DIVIDER_MODULO = 5; // means divider position is every 5th symbol beginning with walknew4
    private static final int CARD_NUMBER_DIVIDER_POSITION = CARD_NUMBER_DIVIDER_MODULO - 1; // means divider position is every 4th symbol beginning with 0
    private static final char CARD_NUMBER_DIVIDER = '-';
    @BindView(R.id.cardno)
    EditText cardno;
    @BindView(R.id.cardDateEditText)
    EditText cardDateEditText;
    @BindView(R.id.cardCVCEditText)
    EditText cardCVCEditText;
    SharedPreferences pref;
    SharedPreferences.Editor editor;
    private static final int CARD_DATE_TOTAL_SYMBOLS = 7; // size of pattern MM/YY
    private static final int CARD_DATE_TOTAL_DIGITS = 6; // max numbers of digits in pattern: MM + YY
    private static final int CARD_DATE_DIVIDER_MODULO = 3; // means divider position is every 3rd symbol beginning with walknew4
    private static final int CARD_DATE_DIVIDER_POSITION = CARD_DATE_DIVIDER_MODULO - 1; // means divider position is every 2nd symbol beginning with 0
    private static final char CARD_DATE_DIVIDER = '/';

    private static final int CARD_CVC_TOTAL_SYMBOLS = 3;
    String card_date, card_year, card_month, card_cvv, card_num, user_id, card_type = "debitcard";

    @BindView(R.id.btn_add_card)
    Button btn_add_card;
    @BindView(R.id.btn_back)
    Button btn_back;
    Context context;
    ProgressDialog progressDialog;
    ApiService apiService;
    String driverid, id, usertype, c_id, booking_id;
    EditText card_numm, card_yearm, card_cvvm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_card_details);
        ButterKnife.bind(this);
        context = CardDetailsActivity.this;
        Log.e("TAG", "Inside Card Details Activity:");
        apiService = RetrofitSingleton.getApiService();
        usertype = PrefConnect.readString(CardDetailsActivity.this, PrefConnect.USERTYPE, "");
        pref = getApplicationContext().getSharedPreferences("MyPref", 0);
        editor = pref.edit();

        id = pref.getString("driver_id", "");
        Log.e("driver_id", id);
        try {
            if (getIntent() != null) {

                driverid = getIntent().getStringExtra("Driverid");
                booking_id = getIntent().getStringExtra("bookingid");


            }

        } catch (Exception e) {
            e.printStackTrace();
            Log.e("paymentact", e.getMessage());

        }


        cardno.getText().toString().trim();
        getWindow().setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

        cardno.addTextChangedListener(this);
        cardDateEditText.addTextChangedListener(this);

        card_numm = (EditText) findViewById(R.id.cardno);
        card_yearm = (EditText) findViewById(R.id.cardDateEditText);
        card_cvvm = (EditText) findViewById(R.id.cardCVCEditText);


    }

    @OnClick({R.id.btn_add_card, R.id.btn_back})
    public void OnClick(View view) {
        switch (view.getId()) {

            case R.id.btn_back:
                finish();
                break;

            case R.id.btn_add_card:
                View view1 = getCurrentFocus();
                Log.e("TAG", "Inside Card Details Activity Button Add Card ");
                Validation();

                break;
        }
    }





    private boolean isInputCorrect(Editable s, int size, int dividerPosition, char divider) {
        boolean isCorrect = s.length() <= size;
        for (int i = 0; i < s.length(); i++) {
            if (i > 0 && (i + 1) % dividerPosition == 0) {
                isCorrect &= divider == s.charAt(i);
            } else {
                isCorrect &= Character.isDigit(s.charAt(i));
            }
        }
        return isCorrect;
    }

    private String concatString(char[] digits, int dividerPosition, char divider) {
        final StringBuilder formatted = new StringBuilder();

        for (int i = 0; i < digits.length; i++) {
            if (digits[i] != 0) {
                formatted.append(digits[i]);
                if ((i > 0) && (i < (digits.length - 1)) && (((i + 1) % dividerPosition) == 0)) {
                    formatted.append(divider);
                }
            }
        }

        return formatted.toString();
    }

    private char[] getDigitArray(final Editable s, final int size) {
        char[] digits = new char[size];
        int index = 0;
        for (int i = 0; i < s.length() && index < size; i++) {
            char current = s.charAt(i);
            if (Character.isDigit(current)) {
                digits[index] = current;
                index++;
            }
        }
        return digits;
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable s) {
        card_num = String.valueOf(s);

        Log.i("TAG", "Card Date:" + s);
        Log.i("TAG", "Card Date :" + cardDateEditText.getEditableText());

        if (s == cardno.getEditableText()) {
            if (!isInputCorrect(s, CARD_NUMBER_TOTAL_SYMBOLS, CARD_NUMBER_DIVIDER_MODULO, CARD_NUMBER_DIVIDER)) {
                s.replace(0, s.length(), concatString(getDigitArray(s, CARD_NUMBER_TOTAL_DIGITS), CARD_NUMBER_DIVIDER_POSITION, CARD_NUMBER_DIVIDER));

            }
            // DO STH
        } else if (s == cardDateEditText.getEditableText()) {
            if (!isInputCorrect(s, CARD_DATE_TOTAL_SYMBOLS, CARD_DATE_DIVIDER_MODULO, CARD_DATE_DIVIDER)) {
                s.replace(0, s.length(), concatString(getDigitArray(s, CARD_DATE_TOTAL_DIGITS), CARD_DATE_DIVIDER_POSITION, CARD_DATE_DIVIDER));

            } else if (s == cardCVCEditText.getEditableText()) {
                if (s.length() > CARD_CVC_TOTAL_SYMBOLS) {
                    s.delete(CARD_CVC_TOTAL_SYMBOLS, s.length());

                }

            }


        }
    }
}

Solution

  • As by my understanding, your date divider must always be at position 2

    private static final int CARD_DATE_DIVIDER_POSITION = CARD_DATE_DIVIDER_MODULO - 1; // means divider position is every 2nd symbol beginning with 0
    

    with the above declaration, before appending the date divider, I should test this way ( you can change your isInputCorrect method this way):

     private boolean isInputCorrect(Editable s, int size, int dividerPosition, char divider) {
            boolean isCorrect = s.length() <= size;
    
            for (int i = 0; i < s.length(); i++) {
                if (i > 0 && (i + 1) % dividerPosition == 0) {
                    if (divider=='/') {
                        if (i==2) {
                            isCorrect &= divider == s.charAt(i);
                        }
                    }
    
                } else {
                    isCorrect &= Character.isDigit(s.charAt(i));
    
                }
            }
            return isCorrect;
        }
    

    And then after the text has changed can be modified a bit to become:

    @Override
        public void afterTextChanged(Editable s) {
            card_num = String.valueOf(s);
            String ss=cardDateEditText.getText().toString();
    
            Log.i("TAG", "Card Date:" + s);
            Log.i("TAG", "Card Date :" + ss);
    
            if (s == cardno.getEditableText()) {
                if (!isInputCorrect(s, CARD_NUMBER_TOTAL_SYMBOLS, CARD_NUMBER_DIVIDER_MODULO, CARD_NUMBER_DIVIDER)) {
                    s.replace(0, s.length(), concatString(getDigitArray(s, CARD_NUMBER_TOTAL_DIGITS), CARD_NUMBER_DIVIDER_POSITION, CARD_NUMBER_DIVIDER));
    
                }
            } else if (s == cardDateEditText.getEditableText()) {
                if (s.length()<=CARD_DATE_TOTAL_SYMBOLS)
                {
                    if (!isInputCorrect(s, CARD_DATE_TOTAL_SYMBOLS, CARD_DATE_DIVIDER_MODULO, CARD_DATE_DIVIDER)) {
                        Log.e("TAG",s+"");
    
                        s.replace(0, s.length(), concatString(getDigitArray(s, CARD_DATE_TOTAL_DIGITS), CARD_DATE_DIVIDER_POSITION, CARD_DATE_DIVIDER));
    
                    }
                }else
                {
                    s.delete(CARD_DATE_TOTAL_SYMBOLS,s.length());
                }
    
    
            } else if (s == cardCVCEditText.getEditableText()) {
                    if (s.length() > CARD_CVC_TOTAL_SYMBOLS) {
                        s.delete(CARD_CVC_TOTAL_SYMBOLS, s.length());
    
                    }
    
                }
    
    
            }
    

    So you test also the length of the editable text.

    The date divider must be included only once and just after the month represented by two first characters. That means the last digit must be at 2nd position index 1, at that instant i==1. So no other divider will be appended. I should have to test if the divider set is for date, to know I'm dealing with the date and then test if the index is not greater or equals to 2.

    That's how I could handle that. I hope this will help.