Open SimonMarquis opened 7 months ago
Just to confirm, were you not seeing any of these crashes on an older version of telephoto?
Can you also share your version of Compose UI?
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.
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.
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.
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.
I just released https://github.com/saket/telephoto/releases/tag/0.10.0. Can you please give it a try?
Our app update will be rolled-out starting next monday 🚀
@SimonMarquis were you able to verify if the frequency of crashes has decreased?
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:
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) }
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.
@SimonMarquis can you share more data about these devices, including their API versions?
Devices:
OS:
This OS distribution makes it look like a platform bug that has been fixed in Android 11.
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.
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?
We have minSdk set to 24 (Android 7)
I've filed a bug report on D8: https://issuetracker.google.com/u/1/issues/338381315.
@SimonMarquis will you be able to share your apk here? https://issuetracker.google.com/issues/338381315#comment2
@SimonMarquis are you observing these crashes in telephoto 0.12.1
by any chance?
I'll take a look at the reports next monday, but we had to downgrade from 0.12.0
to 0.11.2
because of a bigger crash during the first hours of the rollout:
Fatal Exception: java.lang.IllegalStateException
another transformation is already in progress
me.saket.telephoto.zoomable.RealZoomableState.animateSettlingOfZoomOnGestureEnd$zoomable_release (RealZoomableState.kt:124)
me.saket.telephoto.zoomable.ZoomableNode$onTransformStopped$1$1.invokeSuspend (Zoomable.kt:48)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:9)
kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:93)
androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch (AndroidUiDispatcher.android.kt:15)
androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch (AndroidUiDispatcher.android.kt:15)
androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run (AndroidUiDispatcher.android.kt:3)
android.os.Handler.handleCallback (Handler.java:938)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loop (Looper.java:223)
android.app.ActivityThread.main (ActivityThread.java:7780)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:952)
I did not have time to investigate if this was a mis-use of the Composable or an error on the library itself.
That doesn't look good. Can you share your usage of Zoomable*Image()
and its APIs? That error message exposes a bug that was possibly already present in previous versions (including 0.11.2
).
Let's move our discussion for this new crash to https://github.com/saket/telephoto/issues/96
We are only using the Modifier.zoomable()
APIs, like this:
This ZoomableImageGallery
Composable is used inside a androidx.compose.foundation.pager.HorizontalPager
.
And the Image
is a Design System wrapper around Coil's SubcomposeAsyncImage
.
@Composable
fun ZoomableImageGallery(
uri: String,
modifier: Modifier = Modifier,
index: Int? = null,
pagerState: PagerState? = null,
) {
var isImageOnError by remember { mutableStateOf(false) }
var retryHash by remember { mutableIntStateOf(0) }
val zoomState = rememberZoomableState(ZoomSpec(maxZoomFactor = 4.0f))
val imageRequest = ImageRequest.Builder(LocalContext.current)
.data(uri)
.setParameter(key = "retry_hash", value = retryHash, memoryCacheKey = null)
.build()
Image(
model = imageRequest,
modifier = modifier
.fillMaxSize()
.zoomable(
state = zoomState,
onClick = { if (isImageOnError) retryHash++ }
),
onState = { state -> isImageOnError = state is State.Error },
)
// Reset zoom state when the page is moved out of the window.
val isVisible = index == pagerState?.settledPage
LaunchedEffect(isVisible) {
if (!isVisible) zoomState.resetZoom(withAnimation = false)
}
}
Hi @SimonMarquis, can you give 0.14.0 a try? https://github.com/saket/telephoto/releases/tag/0.14.0
I'll bump to 0.14.0 in our dev builds on monday, and if everything looks ok it will be part of the official release (next monday).
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):
or this (with a tiny value, that looks like a 0 approximation in float):
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: