diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-05-07 01:29:03 +0000 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-05-11 13:15:50 +0000 |
| commit | 12037132899485d8ce9d79fa3005e280325f6a50 (patch) | |
| tree | 3e8da853669a77ceaa3fb2d7f8581c2f00d1851b /android/src | |
| parent | b1e8a92d8fbabea29d76aa782663b4388cef8223 (diff) | |
| download | mullvadvpn-12037132899485d8ce9d79fa3005e280325f6a50.tar.xz mullvadvpn-12037132899485d8ce9d79fa3005e280325f6a50.zip | |
Create `SegmentedInputFormatter` helper class
Diffstat (limited to 'android/src')
| -rw-r--r-- | android/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt new file mode 100644 index 0000000000..ed471778eb --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt @@ -0,0 +1,137 @@ +package net.mullvad.mullvadvpn.util + +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText + +class SegmentedInputFormatter(val input: EditText, var separator: Char) : TextWatcher { + private var editing = false + private var removing = false + private var separatorSkipCount = 5 + + var allCaps = false + var isValidInputCharacter: (Char) -> Boolean = { _ -> true } + + var segmentSize = 4 + set(value) { + field = value + separatorSkipCount = value + 1 + } + + init { + input.addTextChangedListener(this) + } + + override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { + if (!editing) { + editing = true + removing = after < count + } + } + + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {} + + override fun afterTextChanged(text: Editable) { + val string = text.toString() + + if (isValidInput(string)) { + editing = false + maybeUpdateSelection() + } else { + formatInput(text) + } + } + + private fun maybeUpdateSelection() { + if (removing) { + var start = input.selectionStart + var end = input.selectionEnd + var changed = false + + if (start % separatorSkipCount == 0) { + start -= 1 + changed = true + } + + if (end % separatorSkipCount == 0) { + end -= 1 + changed = true + } + + if (changed) { + input.setSelection(start, end) + } + } + } + + private fun isValidInput(string: String): Boolean { + return string + .asSequence() + .withIndex() + .all { item -> + val index = item.index + val character = item.value + + if ((index + 1) % separatorSkipCount == 0) { + character == separator + } else { + isValidInputCharacter(character) + } + } + } + + private fun formatInput(input: Editable) { + var index = 0 + val length = input.length + var changed = false + + while (index < length && !changed) { + val segmentStart = index + val segmentEnd = index + segmentSize - 1 + val separatorPosition = segmentEnd + 1 + + changed = formatSegment(input, segmentStart..segmentEnd) || + formatSeparator(input, separatorPosition) + + index = separatorPosition + 1 + } + } + + private fun formatSegment(input: Editable, range: IntRange): Boolean { + val length = input.length + val start = range.start + var end = range.endInclusive + + if (start < length) { + end = minOf(end, length - 1) + + for (index in start..end) { + val character = input[index] + + if (allCaps && character >= 'a' && character <= 'z') { + input.replace(index, index + 1, character.toString().toUpperCase()) + } else if (!isValidInputCharacter(character)) { + input.delete(index, index + 1) + } else { + // Only continue looping if no changes were made to the string + continue + } + + // Abort loop because the input was edited and `afterTextChanged` will be called + // again + return true + } + } + + return false + } + + private fun formatSeparator(input: Editable, index: Int): Boolean { + if (index < input.length && input[index] != '-') { + input.insert(index, "-") + return true + } else { + return false + } + } +} |
