saket / telephoto

Building blocks for designing media experiences in Compose UI
https://saket.github.io/telephoto/
Apache License 2.0
867 stars 28 forks source link

IllegalStateException: maximumVelocity should be a positive value #81

Open SimonMarquis opened 2 months ago

SimonMarquis commented 2 months ago

We are seeing a pretty substantial amount of errors in the latest version (0.9.0) that leads to this method:

https://github.com/saket/telephoto/blob/3e0c5573151e64908a024425bda9cd0be19f4d68/zoomable/src/commonMain/kotlin/me/saket/telephoto/zoomable/internal/transformable.kt#L152

And all the stacktraces look like this (with value 0.0):

Fatal Exception: java.lang.IllegalStateException
maximumVelocity should be a positive value. You specified=0.0
androidx.compose.ui.input.pointer.util.VelocityTracker1D.calculateVelocity (VelocityTracker.kt:283)
androidx.compose.ui.input.pointer.util.VelocityTracker.calculateVelocity-AH228Gc (VelocityTracker.kt:102)
me.saket.telephoto.zoomable.internal.TransformableNode$pointerInputNode$1$1$2.invokeSuspend (transformable.kt:152)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:175)
kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:164)

or this (with a tiny value, that looks like a 0 approximation in float):

Fatal Exception: java.lang.IllegalStateException
maximumVelocity should be a positive value. You specified=-1.3029473E-34
androidx.compose.ui.input.pointer.util.VelocityTracker1D.calculateVelocity (VelocityTracker.kt:283)
androidx.compose.ui.input.pointer.util.VelocityTracker.calculateVelocity-AH228Gc (VelocityTracker.kt:102)
me.saket.telephoto.zoomable.internal.TransformableNode$pointerInputNode$1$1$2.invokeSuspend (transformable.kt:152)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:175)
kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:164)

I'm not sure why it would throw here to be honest, since we always provide sane values Velocity(Int.MAX_VALUE.toFloat(), Int.MAX_VALUE.toFloat()). Could this be an issue with the devices packing floats incorrectly?

The device repartition is as follow:

saket commented 2 months ago

Just to confirm, were you not seeing any of these crashes on an older version of telephoto?

saket commented 2 months ago

Can you also share your version of Compose UI?

SimonMarquis commented 2 months ago

Oh you're right, we did have them on previous versions as well (Crashlytics groupped them differently):

androidx.compose.ui.input.pointer.util.VelocityTracker1D.calculateVelocity (VelocityTracker.kt:283)
androidx.compose.ui.input.pointer.util.VelocityTracker.calculateVelocity-AH228Gc (VelocityTracker.kt:102)
androidx.compose.ui.input.pointer.util.VelocityTracker.calculateVelocity-9UxMQ8M (VelocityTracker.kt:82)
me.saket.telephoto.zoomable.internal.TransformableKt.calculateVelocity-OGnQdUo (transformable.kt:267)
me.saket.telephoto.zoomable.internal.TransformableKt.access$calculateVelocity-OGnQdUo (transformable.kt:1)
me.saket.telephoto.zoomable.internal.TransformableNode$pointerInputNode$1$1$2.invokeSuspend (transformable.kt:156)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:175)
kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:164)

Our Compose UI version is currently at 1.6.4.

saket commented 2 months ago

I've replaced my manual calculation of maximum velocity with the framework one in https://github.com/saket/telephoto/commit/32309870a59a14d84be87b1a6cc39dc6be79b781. Let's see if this fixes the issue.

https://github.com/saket/telephoto/blob/71b96177f293dcb27b64a456894871534b31ea8b/zoomable/src/commonMain/kotlin/me/saket/telephoto/zoomable/internal/transformable.kt#L154-L156

I'm not certain, but https://github.com/saket/telephoto/pull/76 could also help.

I'll cut a release soon. In the meantime, the changes are available in 0.10.0-SNAPSHOT if your project is comfortable using snapshots.

SimonMarquis commented 2 months ago

Thanks for these updates. Unfortunately I won't be able to use such SNAPSHOT version in the main project for now 😖 I'll try to see if we can fork and test this patch.

saket commented 2 months ago

I just released https://github.com/saket/telephoto/releases/tag/0.10.0. Can you please give it a try?

SimonMarquis commented 2 months ago

Our app update will be rolled-out starting next monday 🚀

saket commented 2 months ago

@SimonMarquis were you able to verify if the frequency of crashes has decreased?

SimonMarquis commented 2 months ago

It seems like it did not change anything. New reports are coming up with this new version. To me, the only reason why there could be an error is if [maximumFlingVelocity](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ViewConfiguration#maximumFlingVelocity()) coming from the ViewConfiguration is already negative:

https://github.com/saket/telephoto/blob/7f88d6308209b04ffc0a576a069e3b5562bed71a/zoomable/src/commonMain/kotlin/me/saket/telephoto/zoomable/internal/transformable.kt#L155

The packing/unpacking of 2 floats in a long seems fine.

Given the uncommon distribution of devices having this bug, this could be an OS bug. And from the docs, nothing really prevents it from being negative after all.

Do you think we could try clamping these values?

          val maximumVelocity = currentValueOf(LocalViewConfiguration)
              .maximumFlingVelocity
              .coerceAtLeast(0F)
              .let { Velocity(it, it) }
saket commented 1 month ago

Oof. Considering that Velocity(Int.MAX_VALUE.toFloat(), Int.MAX_VALUE.toFloat()) was also causing the same crash, I don't think the values provided by ViewConfiguration are invalid...

I'll file a bug report.

saket commented 1 month ago

@SimonMarquis can you share more data about these devices, including their API versions?

SimonMarquis commented 1 month ago

Devices:

OS:

This OS distribution makes it look like a platform bug that has been fixed in Android 11.

saket commented 1 month ago

It very likely is! Interestingly, @jakewharton reminded me yesterday that we encountered a similar bug in Cash App a year ago. It's amazing that I've already run into it again.

screenshot_2024-05-01_at_1.35.17___pm.png

I'll file a bug report on D8, and think of a way to suppress these in telephoto.

Is Android 8 your min sdk version?

SimonMarquis commented 1 month ago

We have minSdk set to 24 (Android 7)

saket commented 1 month ago

I've filed a bug report on D8: https://issuetracker.google.com/u/1/issues/338381315.

saket commented 1 month ago

@SimonMarquis will you be able to share your apk here? https://issuetracker.google.com/issues/338381315#comment2