dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22k stars 1.72k forks source link

Toggling FlyoutLayoutBehavior on Android causes the app to crash #18161

Closed mjo151 closed 2 months ago

mjo151 commented 11 months ago

Description

Toggling the FlyoutLayoutBehavior property on a Flyout page on Android causes the app to crash. The problem occurs in .NET 8 SR1 and SR2, and this problem also existed in .NET 7.

Steps to Reproduce

  1. Clone the linked repository
  2. Run the app on an Android tablet
  3. Click the "Toggle Flyout Behavior" button
  4. Notice the flyout menu closes (as expected)
  5. Expand the flyout menu and click the "Toggle Flyout Behavior" button
  6. Notice the app crashes

Link to public reproduction project repository

https://github.com/mjo151/maui-flyout-behavior-bug

Version with bug

8.0.0-rc.1.9171

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 13 (did not test other Android versions)

Did you find any workaround?

I did not find a workaround for this issue.

Relevant log output

Java.Lang.ClassCastException: androidx.appcompat.widget.LinearLayoutCompat$LayoutParams cannot be cast to androidx.drawerlayout.widget.DrawerLayout$LayoutParams
  at java.lang.ClassCastException: androidx.appcompat.widget.LinearLayoutCompat$LayoutParams cannot be cast to androidx.drawerlayout.widget.DrawerLayout$LayoutParams
  at at androidx.drawerlayout.widget.DrawerLayout.getDrawerViewAbsoluteGravity(DrawerLayout.java:991)
  at at androidx.drawerlayout.widget.DrawerLayout.checkDrawerViewAbsoluteGravity(DrawerLayout.java:996)
  at at androidx.drawerlayout.widget.DrawerLayout$ViewDragCallback.onViewPositionChanged(DrawerLayout.java:2292)
  at at androidx.customview.widget.ViewDragHelper.continueSettling(ViewDragHelper.java:779)
  at at androidx.drawerlayout.widget.DrawerLayout.computeScroll(DrawerLayout.java:1375)
  at at android.view.View.updateDisplayListIfDirty(View.java:22044)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.updateDisplayListIfDirty(View.java:22052)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.updateDisplayListIfDirty(View.java:22052)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.updateDisplayListIfDirty(View.java:22052)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.updateDisplayListIfDirty(View.java:22052)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.updateDisplayListIfDirty(View.java:22052)
  at at android.view.View.draw(View.java:22925)
  at at android.view.ViewGroup.drawChild(ViewGroup.java:4529)
  at at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4290)
  at at android.view.View.draw(View.java:23197)
  at at com.android.internal.policy.DecorView.draw(DecorView.java:821)
  at at android.view.View.updateDisplayListIfDirty(View.java:22061)
  at at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:689)
  at at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:695)
  at at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:793)
  at at android.view.ViewRootImpl.draw(ViewRootImpl.java:4670)
  at at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4381)
  at at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3600)
  at at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2328)
  at at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9087)
  at at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
  at at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
  at at android.view.Choreographer.doCallbacks(Choreographer.java:899)
  at at android.view.Choreographer.doFrame(Choreographer.java:832)
  at at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
  at at android.os.Handler.handleCallback(Handler.java:942)
  at at android.os.Handler.dispatchMessage(Handler.java:99)
  at at android.os.Looper.loopOnce(Looper.java:201)
  at at android.os.Looper.loop(Looper.java:288)
  at at android.app.ActivityThread.main(ActivityThread.java:7872)
  at at java.lang.reflect.Method.invoke(Native Method)
  at at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
ghost commented 11 months ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

prabhavmehra commented 8 months ago

any updates on this? Facing same issue with a very simple flyout behaviour.

guyvaio commented 1 month ago

Error probably not fixed as written just above.

After changing the orientation of the tablet, exactly the same error occurs (same stack but with different line numbers)

guyvaio commented 1 month ago

I've created a minimal MAUI 8.0.80 app with a Flyoutpage. All test are done on a physical tablet. To reproduce the problem, just switch from landscape to portrait, and from portrait to landscape. And repeat until the crash occurs. Sometime the crash occurs immediately, sometime after a dozen of rotations. It seems related to the speed of the rotation. Sometime, in landscape mode, the split mode does not appear. And at the next rotation back from portrait, it will appear again. The crash occurs when rotating from portrait to landscape. It seems to be consistent with the cast exception. The panel layout is already a linear panel and the android native code seems to handle at this time the popover panel.

When considering the FlyoutPageRenderer of Xamarin (was working perfectly) https://github.com/xamarin/Xamarin.Forms/blob/5.0.0/Xamarin.Forms.Platform.Android/AppCompat/FlyoutPageRenderer.cs I notice in the code 2 hacks with await Task.Delay(100);

There is no such code in the MAUI FlyoutViewHandler https://github.com/dotnet/maui/blob/main/src/Core/src/Handlers/FlyoutView/FlyoutViewHandler.Android.cs

I don't know if it is related.

The reality however is that it is impossible to release an app on a tablet that crashes when you change the orientation.

guyvaio commented 1 month ago

After investigations, it appears the root cause of this problem is in Maui-main\src\Essentials\src\DeviceDisplay\DeviceDisplay.android.cs

public override async void OnOrientationChanged(int orientation)
{
    await Task.Delay(500);
    onChanged();
}

The code is awaiting half a second (why so long?) but if in the interval another rotation occurs, the FlyoutPage enter in race conditions.

This is not an acceptable solution but if I replace OnOrientationChanged by this code, the error does not occur anymore. And it solves other errors like split mode visible in portrait.

private bool changeIsBusy;
public override async void OnOrientationChanged(int orientation)
{
    if (!changeIsBusy)
    {
        try
        {
            changeIsBusy = true;
            await Task.Delay(500);
            onChanged();
        }
        finally
        {
            changeIsBusy = false;
        }
    }
}

Hoping this will help someone to provide a real solution.

guyvaio commented 1 month ago

Fix proposed: https://github.com/dotnet/maui/compare/main...guyvaio:maui:patch-1

Brosten commented 2 weeks ago

Will this make it to SR9?

guyvaio commented 1 week ago

Temporary fix https://gist.github.com/guyvaio/5ca11a6aa373c1fe486cc9e25137aeec Hope this helps others.

Brosten commented 1 week ago

Why is this issue closed? I see no open issue addressing it. The crash is rather easy to reproduce with latest MAUI. The workaround by @guyvaio sadly didn't fix it for us either.. :(

mfazucchi commented 6 days ago

I am also still experiencing this issue on an Android tablet using the latest .NET 8 and 8.0.82 maui compatibility packages.