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}