class PhoneNumberVisualTransformation(formatter: AsYouTypeFormatter) : VisualTransformation {
private val phoneNumberFormatter = formatter
override fun filter(text: AnnotatedString): TransformedText {
val transformation = reformat("+$text", Selection.getSelectionEnd("+$text"))
val span = buildAnnotatedString {
val str = transformation.formatted?.replace("+", "") ?: ""
val firstSpaceIndex = str.indexOfFirst { it == ' ' }
append(str)
if (firstSpaceIndex == -1 && str.isNotEmpty() && str.length < 4) {
addStyle(style = SpanStyle(color = Color(0xff999A9F)), start = 0, str.length)
} else if (firstSpaceIndex != -1) {
addStyle(style = SpanStyle(color = Color(0xff999A9F)), start = 0, firstSpaceIndex)
}
}
return TransformedText(span, object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
return transformation.originalToTransformed.getOrNull(offset) ?: 0
}
override fun transformedToOriginal(offset: Int): Int {
return transformation.transformedToOriginal[offset]
}
})
}
private fun reformat(s: CharSequence, cursor: Int): Transformation {
phoneNumberFormatter.clear()
val curIndex = cursor - 1
var formatted: String? = null
var lastNonSeparator = 0.toChar()
var hasCursor = false
s.forEachIndexed { index, char ->
if (PhoneNumberUtils.isNonSeparator(char)) {
if (lastNonSeparator.code != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor)
hasCursor = false
}
lastNonSeparator = char
}
if (index == curIndex) {
hasCursor = true
}
}
if (lastNonSeparator.code != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor)
}
val originalToTransformed = mutableListOf<Int>()
val transformedToOriginal = mutableListOf<Int>()
var specialCharsCount = 0
formatted?.forEachIndexed { index, char ->
if (!PhoneNumberUtils.isNonSeparator(char)) {
specialCharsCount++
} else {
originalToTransformed.add(index)
}
transformedToOriginal.add(index - specialCharsCount)
}
originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)
return Transformation(formatted?.replace("+", ""), originalToTransformed, transformedToOriginal)
}
private fun getFormattedNumber(lastNonSeparator: Char, hasCursor: Boolean): String? {
return if (hasCursor) {
phoneNumberFormatter.inputDigitAndRememberPosition(lastNonSeparator)
} else {
phoneNumberFormatter.inputDigit(lastNonSeparator)
}
}
private data class Transformation(
val formatted: String?,
val originalToTransformed: List<Int>,
val transformedToOriginal: List<Int>
)
}