Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ public void attachToView(@NonNull FlutterView flutterView) {
*/
public void detachFromView() {
destroyOverlaySurfaces();
removeOverlaySurfaces();
this.flutterView = null;
flutterViewConvertedToImageView = false;

Expand Down Expand Up @@ -970,17 +971,26 @@ public FlutterOverlaySurface createOverlaySurface() {
* Destroys the overlay surfaces and removes them from the view hierarchy.
*
* <p>This method is used only internally by {@code FlutterJNI}.
*
* <p>This member is not intended for public use, and is only visible for testing.
*/
public void destroyOverlaySurfaces() {
for (int i = 0; i < overlayLayerViews.size(); i++) {
FlutterImageView overlayView = overlayLayerViews.valueAt(i);
final FlutterImageView overlayView = overlayLayerViews.valueAt(i);
overlayView.detachFromRenderer();
overlayView.closeImageReader();
if (flutterView != null) {
flutterView.removeView(overlayView);
}
// Don't remove overlayView from the view hierarchy since this method can
// be called while the Android framework is iterating over the array of views.
// See ViewGroup#dispatchDetachedFromWindow(), and
// https://github.com/flutter/flutter/issues/97679.
}
}

private void removeOverlaySurfaces() {
if (flutterView == null) {
Log.e(TAG, "removeOverlaySurfaces called while flutter view is null");
return;
}
for (int i = 0; i < overlayLayerViews.size(); i++) {
flutterView.removeView(overlayLayerViews.valueAt(i));
}
overlayLayerViews.clear();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,17 +657,13 @@ public void detach__destroysOverlaySurfaces() {

platformViewsController.detach();

assertThrows(
IllegalStateException.class,
() -> {
platformViewsController.onDisplayOverlaySurface(
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
});
verify(overlayImageView, times(1)).closeImageReader();
verify(overlayImageView, times(1)).detachFromRenderer();
}

@Test
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
public void detachFromView__removesOverlaySurfaces() {
public void detachFromView__removesAndDestroysOverlayViews() {
final PlatformViewsController platformViewsController = new PlatformViewsController();

final int platformViewId = 0;
Expand All @@ -684,6 +680,9 @@ public void detachFromView__removesOverlaySurfaces() {
jni.attachToNative();
attach(jni, platformViewsController);

final FlutterView flutterView = mock(FlutterView.class);
platformViewsController.attachToView(flutterView);

final FlutterImageView overlayImageView = mock(FlutterImageView.class);
when(overlayImageView.acquireLatestImage()).thenReturn(true);

Expand All @@ -695,17 +694,14 @@ public void detachFromView__removesOverlaySurfaces() {

platformViewsController.detachFromView();

assertThrows(
IllegalStateException.class,
() -> {
platformViewsController.onDisplayOverlaySurface(
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
});
verify(overlayImageView, times(1)).closeImageReader();
verify(overlayImageView, times(1)).detachFromRenderer();
verify(flutterView, times(1)).removeView(overlayImageView);
}

@Test
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {
public void destroyOverlaySurfaces__doesNotThrowIfFlutterViewIsDetached() {
final PlatformViewsController platformViewsController = new PlatformViewsController();

final int platformViewId = 0;
Expand All @@ -722,6 +718,9 @@ public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {
jni.attachToNative();
attach(jni, platformViewsController);

final FlutterView flutterView = mock(FlutterView.class);
platformViewsController.attachToView(flutterView);

final FlutterImageView overlayImageView = mock(FlutterImageView.class);
when(overlayImageView.acquireLatestImage()).thenReturn(true);

Expand All @@ -735,6 +734,42 @@ public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {

platformViewsController.destroyOverlaySurfaces();
verify(overlayImageView, times(1)).closeImageReader();
verify(overlayImageView, times(1)).detachFromRenderer();
}

@Test
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
public void destroyOverlaySurfaces__doesNotRemoveOverlayView() {
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();
attach(jni, platformViewsController);

final FlutterView flutterView = mock(FlutterView.class);
platformViewsController.attachToView(flutterView);

final FlutterImageView overlayImageView = mock(FlutterImageView.class);
when(overlayImageView.acquireLatestImage()).thenReturn(true);

final FlutterOverlaySurface overlaySurface =
platformViewsController.createOverlaySurface(overlayImageView);

platformViewsController.onDisplayOverlaySurface(
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);

platformViewsController.destroyOverlaySurfaces();
verify(flutterView, never()).removeView(overlayImageView);
}

@Test
Expand Down