Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visual transformation causing the app to crash on compose v1.7.2 #65

Open
rungxanh1995 opened this issue Sep 20, 2024 · 1 comment
Open

Comments

@rungxanh1995
Copy link

rungxanh1995 commented Sep 20, 2024

Describe the bug
Recently, entering phone number to the text field is crashing the app.

For example: I want to enter a Canadian phone number +14165550000. The code will crash as soon as I've entered "4165550" into the text field.

Important note: The code runs well on its own, without any extra dependencies or dependency version upgrades. Then I integrated the library to my project, updated dependency versions and made some necessary edits, it's when the issue arise. The visual transformation logic no longer works in a reliable manner, crashing the app as offset mapping is now flawed.

Refer to my Github fork commits to see the steps I made to edit visual transformation logic to ensure the app doesn't crash.

To Reproduce
Steps to reproduce the behavior:

  1. Tap the country flag to pick Canada
  2. Tap the text field to start entering your phone number
  3. Type 4165550
  4. Type the next number and observe the app crashes

Expected behavior
The app shouldn't have crashed, but instead handle visually transforming the input as before.

Smartphone (please complete the following information):

  • Device: Pixel 8a
  • OS: Android 14

Additional context
Refer to the stack trace error below

FATAL EXCEPTION: main (Ask Gemini)
Process: com.togitech.togii, PID: 15552
java.lang.IllegalStateException: OffsetMapping.transformedToOriginal returned invalid mapping: 0 -> -1 is not in range of original text [0, 8]
	at androidx.compose.foundation.text.ValidatingOffsetMappingKt.validateTransformedToOriginal(ValidatingOffsetMapping.kt:116)
	at androidx.compose.foundation.text.ValidatingOffsetMappingKt.throwIfNotValidTransform(ValidatingOffsetMapping.kt:73)
	at androidx.compose.foundation.text.ValidatingOffsetMappingKt.throwIfNotValidTransform$default(ValidatingOffsetMapping.kt:60)
	at androidx.compose.foundation.text.ValidatingOffsetMappingKt.filterWithValidation(ValidatingOffsetMapping.kt:35)
	at androidx.compose.foundation.text.CoreTextFieldKt.CoreTextField(CoreTextField.kt:246)
	at androidx.compose.foundation.text.BasicTextFieldKt.BasicTextField(BasicTextField.kt:765)
	at androidx.compose.material.OutlinedTextFieldKt.OutlinedTextField(OutlinedTextField.kt:379)
	at com.togitech.ccp.component.TogiCountryCodePickerKt.TogiCountryCodePicker(TogiCountryCodePicker.kt:173)
	at com.togitech.togii.MainActivityKt.CountryCodePick(MainActivity.kt:77)
	at com.togitech.togii.MainActivityKt$CountryCodePick$2.invoke(Unknown Source:8)
	at com.togitech.togii.MainActivityKt$CountryCodePick$2.invoke(Unknown Source:10)
	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:192)
	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2825)
	at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:3116)
	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3607)
	at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3552)
	at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:948)
	at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1206)
	at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:132)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:616)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:585)
	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
	at android.view.Choreographer.doCallbacks(Choreographer.java:952)
	at android.view.Choreographer.doFrame(Choreographer.java:878)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
	at android.os.Handler.handleCallback(Handler.java:958)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:205)
	at android.os.Looper.loop(Looper.java:294)
	at android.app.ActivityThread.main(ActivityThread.java:8177)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@53d2e8, androidx.compose.ui.platform.MotionDurationScaleImpl@563ad01, StandaloneCoroutine{Cancelling}@9ed27a6, AndroidUiDispatcher@c72a5e7]

@rungxanh1995
Copy link
Author

rungxanh1995 commented Sep 20, 2024

At the moment, I'm using 0 as the fallback index to reformat the input text to not crash the app, as follow:

// PhoneNumberTransformation.kt
@Suppress("AvoidMutableCollections", "AvoidVarsExceptWithDelegate")
    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) // Disabled this line
            transformedToOriginal.add(maxOf(index - specialCharsCount, 0)) // -> Changed to this line
        }
        originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
        transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)

        return Transformation(formatted, originalToTransformed, transformedToOriginal)
    }

@rungxanh1995 rungxanh1995 changed the title Visual transformation causing the app to crash Visual transformation causing the app to crash on compose v1.7.2 Sep 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant