diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 862c63d9de9db..f2dc0f8eebf61 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -473,7 +473,6 @@ public void detach() { if (platformViewsChannel != null) { platformViewsChannel.setPlatformViewsHandler(null); } - destroyOverlaySurfaces(); platformViewsChannel = null; context = null; textureRegistry = null; @@ -501,7 +500,6 @@ public void attachToView(@NonNull View flutterView) { * the previously attached {@code View}. */ public void detachFromView() { - destroyOverlaySurfaces(); this.flutterView = null; // Inform all existing platform views that they are no longer associated with @@ -827,7 +825,11 @@ public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) initializeRootImageViewIfNeeded(); final FlutterImageView overlayView = overlayLayerViews.get(id); - if (overlayView.getParent() == null) { + FlutterView overlayViewParent = (FlutterView) overlayView.getParent(); + if (overlayViewParent != flutterView) { + if (overlayViewParent != null) { + overlayViewParent.removeView(overlayView); + } ((FlutterView) flutterView).addView(overlayView); } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 81d884411c0b9..7c3ede9a949cc 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -563,62 +563,7 @@ public void onEndFrame__removesPlatformViewParent() { @Test @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class}) - public void detach__destroysOverlaySurfaces() { - final PlatformViewsController platformViewsController = new PlatformViewsController(); - - final int platformViewId = 0; - assertNull(platformViewsController.getPlatformViewById(platformViewId)); - - final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); - final PlatformView platformView = mock(PlatformView.class); - when(platformView.getView()).thenReturn(mock(View.class)); - when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView); - - platformViewsController.getRegistry().registerViewFactory("testType", viewFactory); - - final FlutterJNI jni = new FlutterJNI(); - jni.attachToNative(false); - attach(jni, platformViewsController); - - jni.onFirstFrame(); - - // Simulate create call from the framework. - createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true); - - // Produce a frame that displays a platform view and an overlay surface. - platformViewsController.onBeginFrame(); - platformViewsController.onDisplayPlatformView( - platformViewId, - /* x=*/ 0, - /* y=*/ 0, - /* width=*/ 10, - /* height=*/ 10, - /* viewWidth=*/ 10, - /* viewHeight=*/ 10, - /* mutatorsStack=*/ new FlutterMutatorsStack()); - - final FlutterImageView overlayImageView = mock(FlutterImageView.class); - when(overlayImageView.acquireLatestImage()).thenReturn(true); - - final FlutterOverlaySurface overlaySurface = - platformViewsController.createOverlaySurface(overlayImageView); - // This is OK. - platformViewsController.onDisplayOverlaySurface( - overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10); - - platformViewsController.detach(); - - assertThrows( - IllegalStateException.class, - () -> { - platformViewsController.onDisplayOverlaySurface( - overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10); - }); - } - - @Test - @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class}) - public void detachFromView__removesOverlaySurfaces() { + public void destroyOverlaySurfaces__removesOverlaySurfaces() { final PlatformViewsController platformViewsController = new PlatformViewsController(); final int platformViewId = 0; @@ -644,7 +589,8 @@ public void detachFromView__removesOverlaySurfaces() { platformViewsController.onDisplayOverlaySurface( overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10); - platformViewsController.detachFromView(); + // Simulate |DestroyLayers| call from SurfacePool. + platformViewsController.destroyOverlaySurfaces(); assertThrows( IllegalStateException.class, @@ -787,6 +733,86 @@ public void dontConverRenderSurfaceWhenFlagIsTrue() { disposePlatformView(jni, platformViewsController, platformViewId); } + @Test + @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class}) + public void onDisplayOverlaySurface__doesNotThrowWhenMultiFlutterViewShareEngine() { + final PlatformViewsController platformViewsController = new PlatformViewsController(); + + final int platformViewId = 0; + assertNull(platformViewsController.getPlatformViewById(platformViewId)); + + final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); + final PlatformView platformView = mock(PlatformView.class); + when(platformView.getView()).thenReturn(mock(View.class)); + when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView); + + platformViewsController.getRegistry().registerViewFactory("testType", viewFactory); + + final FlutterJNI jni = new FlutterJNI(); + jni.attachToNative(false); + final FlutterView flutterView1 = attach(jni, platformViewsController); + + jni.onFirstFrame(); + + // Simulate create call from the framework. + createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true); + + // Produce a frame that displays a platform view and an overlay surface. + platformViewsController.onBeginFrame(); + platformViewsController.onDisplayPlatformView( + platformViewId, + /* x=*/ 0, + /* y=*/ 0, + /* width=*/ 10, + /* height=*/ 10, + /* viewWidth=*/ 10, + /* viewHeight=*/ 10, + /* mutatorsStack=*/ new FlutterMutatorsStack()); + + final FlutterImageView overlayImageView1 = mock(FlutterImageView.class); + when(overlayImageView1.acquireLatestImage()).thenReturn(true); + + final FlutterOverlaySurface overlaySurface1 = + platformViewsController.createOverlaySurface(overlayImageView1); + platformViewsController.onDisplayOverlaySurface( + overlaySurface1.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10); + + platformViewsController.onEndFrame(); + + FlutterEngine flutterEngine = flutterView1.getAttachedFlutterEngine(); + flutterView1.detachFromFlutterEngine(); + + FlutterView flutterView2 = new FlutterView(RuntimeEnvironment.application); + flutterView2.attachToFlutterEngine(flutterEngine); + + jni.onFirstFrame(); + + // Simulate create call from the framework. + createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true); + + // Produce a frame that displays a platform view and an overlay surface. + platformViewsController.onBeginFrame(); + platformViewsController.onDisplayPlatformView( + platformViewId, + /* x=*/ 0, + /* y=*/ 0, + /* width=*/ 10, + /* height=*/ 10, + /* viewWidth=*/ 10, + /* viewHeight=*/ 10, + /* mutatorsStack=*/ new FlutterMutatorsStack()); + + final FlutterImageView overlayImageView2 = mock(FlutterImageView.class); + when(overlayImageView2.acquireLatestImage()).thenReturn(true); + + final FlutterOverlaySurface overlaySurface2 = + platformViewsController.createOverlaySurface(overlayImageView2); + platformViewsController.onDisplayOverlaySurface( + overlaySurface2.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10); + + platformViewsController.onEndFrame(); + } + private static ByteBuffer encodeMethodCall(MethodCall call) { final ByteBuffer buffer = StandardMethodCodec.INSTANCE.encodeMethodCall(call); buffer.rewind();