PinEntryWrapper.java

  1package eu.siacs.conversations.ui.util;
  2
  3import android.text.Editable;
  4import android.text.TextWatcher;
  5import android.view.KeyEvent;
  6import android.view.View;
  7import android.widget.EditText;
  8import android.widget.LinearLayout;
  9
 10import java.util.ArrayList;
 11import java.util.List;
 12import java.util.regex.Pattern;
 13
 14
 15public class PinEntryWrapper {
 16
 17    private static Pattern PIN_STRING_PATTERN = Pattern.compile("^[0-9]{6}$");
 18
 19    private final List<EditText> digits = new ArrayList<>();
 20
 21    private final TextWatcher textWatcher = new TextWatcher() {
 22        @Override
 23        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 24
 25        }
 26
 27        @Override
 28        public void onTextChanged(CharSequence s, int start, int before, int count) {
 29
 30        }
 31
 32        @Override
 33        public void afterTextChanged(Editable s) {
 34            int current = -1;
 35            for (int i = 0; i < digits.size(); ++i) {
 36                if (s.hashCode() == digits.get(i).getText().hashCode()) {
 37                    current = i;
 38                }
 39            }
 40            if (current == -1) {
 41                return;
 42            }
 43            if (s.length() > 0) {
 44                if (current < digits.size() - 1) {
 45                    digits.get(current + 1).requestFocus();
 46                }
 47            } else {
 48                int focusOn = current;
 49                for (int i = current - 1; i >= 0; --i) {
 50                    if (digits.get(i).getText().length() == 0) {
 51                        focusOn = i;
 52                    } else {
 53                        break;
 54                    }
 55                }
 56                digits.get(focusOn).requestFocus();
 57            }
 58        }
 59    };
 60
 61    private final View.OnKeyListener keyListener = (v, keyCode, event) -> {
 62        if (event.getAction() != KeyEvent.ACTION_DOWN) {
 63            return false;
 64        }
 65        if (v instanceof EditText) {
 66            final EditText editText = (EditText) v;
 67            if (keyCode == KeyEvent.KEYCODE_DEL && editText.getText().length() == 0) {
 68                final int current = digits.indexOf(editText);
 69                for (int i = current - 1; i >= 0; --i) {
 70                    if (digits.get(i).getText().length() > 0) {
 71                        digits.get(i).getText().clear();
 72                        return true;
 73                    }
 74                }
 75                if (current != 0) {
 76                    digits.get(0).requestFocus();
 77                    return true;
 78                }
 79            }
 80        }
 81        return false;
 82    };
 83
 84    public PinEntryWrapper(LinearLayout linearLayout) {
 85        for (int i = 0; i < linearLayout.getChildCount(); ++i) {
 86            View view = linearLayout.getChildAt(i);
 87            if (view instanceof EditText) {
 88                this.digits.add((EditText) view);
 89            }
 90        }
 91        registerListeners();
 92    }
 93
 94    private void registerListeners() {
 95        for (EditText editText : digits) {
 96            editText.addTextChangedListener(textWatcher);
 97            editText.setOnKeyListener(keyListener);
 98        }
 99    }
100
101    public String getPin() {
102        char[] chars = new char[digits.size()];
103        for(int i = 0; i < chars.length; ++i) {
104            final String input = digits.get(i).getText().toString();
105            chars[i] = input.length() != 1 ? ' ' : input.charAt(0);
106        }
107        return String.valueOf(chars);
108    }
109
110    public void setPin(String pin) {
111        char[] chars = pin.toCharArray();
112        for(int i = 0; i < digits.size(); ++i) {
113            if (i < chars.length) {
114                final Editable editable = digits.get(i).getText();
115                editable.clear();
116                editable.append(Character.isDigit(chars[i]) ? String.valueOf(chars[i]) : "");
117            }
118        }
119    }
120
121    public boolean isEmpty() {
122        for(EditText digit : digits) {
123            if (digit.getText().length() > 0) {
124                return false;
125            }
126        }
127        return true;
128    }
129
130    public static boolean isPin(CharSequence pin) {
131        return pin != null && PIN_STRING_PATTERN.matcher(pin).matches();
132    }
133
134    public void clear() {
135        for(int i = digits.size() - 1; i >= 0; --i) {
136            digits.get(i).getText().clear();
137        }
138    }
139}