element-hq / element-x-android

Android Matrix messenger application using the Matrix Rust Sdk and Jetpack Compose
GNU Affero General Public License v3.0
1.1k stars 156 forks source link

Compose 1.7.0 crashes the composer #3414

Open jmartinesp opened 2 months ago

jmartinesp commented 2 months ago

Contents

This is an issue to describe what's happening when trying to update to the latest Compose BOM. It seems like the new Compose version is having issues with handling focus between Android Views <--> Compose components.

Ideally we should find a solution for this, but if it can't be done maybe we'll need to simplify the UI somehow.

Steps to reproduce

  1. Use https://github.com/element-hq/element-x-android/pull/3399.
  2. Go into a room, change into the RTE mode. Notice the focus can take a while to be applied to the edit text.
  3. Then close the rich text mode.

Outcome

What did you expect?

The plain text/markdown mode should be displayed back.

What happened instead?

The app crashes with:

Thread: main, Exception: java.lang.IllegalArgumentException: performMeasureAndLayout called during measure layout
at androidx.compose.ui.internal.InlineClassHelperKt.throwIllegalArgumentException(InlineClassHelper.kt:34)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout-0kLqBqw(MeasureAndLayoutDelegate.kt:874)
at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout-0kLqBqw(AndroidComposeView.android.kt:1285)
at androidx.compose.ui.node.LayoutNode.forceRemeasure(LayoutNode.kt:1295)
at androidx.compose.foundation.lazy.LazyListBeyondBoundsState.remeasure(LazyListBeyondBoundsModifier.kt:39)
at androidx.compose.foundation.lazy.layout.LazyLayoutBeyondBoundsModifierLocal.layout-o7g1Pn8(LazyLayoutBeyondBoundsModifierLocal.kt:108)
at androidx.compose.ui.focus.BeyondBoundsLayoutKt.searchBeyondBounds--OM-vw8(BeyondBoundsLayout.kt:45)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.generateAndSearchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:160)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.searchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:188)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.generateAndSearchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:155)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.twoDimensionalFocusSearch-sMXa3k8(TwoDimensionalFocusSearch.kt:81)
at androidx.compose.ui.focus.FocusTraversalKt.focusSearch-0X8WOeE(FocusTraversal.kt:109)
at androidx.compose.ui.focus.FocusOwnerImpl.focusSearch-ULY8qGw(FocusOwnerImpl.kt:247)
at androidx.compose.ui.platform.AndroidComposeView.focusSearch(AndroidComposeView.android.kt:855)
at android.view.ViewGroup.focusSearch(ViewGroup.java:1081)
at android.view.ViewGroup.focusSearch(ViewGroup.java:1081)
at android.view.View.focusSearch(View.java:13798)
at android.widget.TextView.hasEditorInFocusSearchDirection(TextView.java:9722)
at android.widget.TextView.onCreateInputConnection(TextView.java:9748)
at androidx.appcompat.widget.AppCompatEditText.onCreateInputConnection(AppCompatEditText.java:270)
at android.view.inputmethod.InputMethodManager.createInputConnection(InputMethodManager.java:4448)
at android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:2735)
at android.view.inputmethod.InputMethodManager.startInputOnWindowFocusGainInternal(InputMethodManager.java:976)
at android.view.inputmethod.InputMethodManager.checkFocus(InputMethodManager.java:3005)
at android.view.inputmethod.InputMethodManager.restartInput(InputMethodManager.java:2584)
at android.widget.TextView.setKeyListener(TextView.java:2694)
at androidx.appcompat.widget.AppCompatEditText.initEmojiKeyListener(AppCompatEditText.java:151)
at androidx.appcompat.widget.AppCompatEditText.(AppCompatEditText.java:117)
at androidx.appcompat.widget.AppCompatEditText.(AppCompatEditText.java:96)
at androidx.appcompat.widget.AppCompatEditText.(AppCompatEditText.java:92)
at io.element.android.libraries.textcomposer.components.markdown.MarkdownEditText.(MarkdownEditText.kt:24)
at io.element.android.libraries.textcomposer.components.markdown.MarkdownTextInputKt$MarkdownTextInput$1$1.invoke(MarkdownTextInput.kt:93)
at io.element.android.libraries.textcomposer.components.markdown.MarkdownTextInputKt$MarkdownTextInput$1$1.invoke(MarkdownTextInput.kt:92)
at androidx.compose.ui.viewinterop.ViewFactoryHolder.(AndroidView.android.kt:344)
at androidx.compose.ui.viewinterop.AndroidView_androidKt$createAndroidViewNodeFactory$1$1.invoke(AndroidView.android.kt:275)
at androidx.compose.ui.viewinterop.AndroidView_androidKt$createAndroidViewNodeFactory$1$1.invoke(AndroidView.android.kt:274)
at androidx.compose.runtime.changelist.Operation$InsertNodeFixup.execute(Operation.kt:583)
at androidx.compose.runtime.changelist.Operations.executeAndFlushAllPendingOperations(Operations.kt:309)
at androidx.compose.runtime.changelist.FixupList.executeAndFlushAllPendingFixups(FixupList.kt:50)
at androidx.compose.runtime.changelist.Operation$InsertSlotsWithFixups.execute(Operation.kt:550)
at androidx.compose.runtime.changelist.Operations.executeAndFlushAllPendingOperations(Operations.kt:309)
at androidx.compose.runtime.changelist.ChangeList.executeAndFlushAllPendingChanges(ChangeList.kt:81)
at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:984)
at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1013)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1150)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3874)
at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3874)
at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:649)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:635)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(SubcomposeLayout.kt:516)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:488)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:479)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:463)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:895)
at io.element.android.features.messages.impl.ExpandableBottomSheetScaffoldKt$ExpandableBottomSheetScaffold$2$1.invoke-0kLqBqw(ExpandableBottomSheetScaffold.kt:124)
at io.element.android.features.messages.impl.ExpandableBottomSheetScaffoldKt$ExpandableBottomSheetScaffold$2$1.invoke(ExpandableBottomSheetScaffold.kt:123)
at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:725)
at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:135)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:316)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:315)
at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2441)
at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1775)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:696)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1221)
at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release$default(LayoutNode.kt:1212)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:369)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:566)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:534)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:390)
at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:1273)
at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:248)
at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1555)
at android.view.View.draw(View.java:23923)
at android.view.View.updateDisplayListIfDirty(View.java:22787)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4543)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4516)
at android.view.View.updateDisplayListIfDirty(View.java:22743)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:706)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:712)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:810)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:5144)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4835)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4011)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2650)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9529)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1343)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1352)
at android.view.Choreographer.doCallbacks(Choreographer.java:952)
at android.view.Choreographer.doFrame(Choreographer.java:882)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1326)
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:8282)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:554)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:885)

Your phone model

Xiaomi Mi 9T

Operating system version

Android 14

Application version and app store

No response

Homeserver

No response

Will you send logs?

Yes

Are you willing to provide a PR?

Yes

jmartinesp commented 2 months ago

Apparently this can be worked around by overriding this in MarkdownEditText:

override fun focusSearch(direction: Int): View? {
    return null
}

Although I'm not sure about the implications. It should be fine since we don't care about the next focused view at all in this component.

bmarty commented 2 months ago

Will you apply this patch on #3399?

jmartinesp commented 2 months ago

Yes, it's already in use there.