Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ed148a8

Browse files
author
Emmanuel Garcia
committed
Fix crash
1 parent 1ee0285 commit ed148a8

2 files changed

Lines changed: 123 additions & 31 deletions

File tree

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,20 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
7979
// it is associated with(e.g if a platform view creates other views in the same virtual display.
8080
private final HashMap<Context, View> contextToPlatformView;
8181

82+
// The view returned by `PlatformView#getView()`.
83+
//
84+
// This only applies to hybrid composition.
8285
private final SparseArray<View> platformViews;
83-
private final SparseArray<FlutterMutatorView> mutatorViews;
86+
87+
// The platform view parent that is appended to `FlutterView`.
88+
// If an entry in `platformViews` doesn't have an entry in this array, the platform view isn't
89+
// in the view hierarchy.
90+
//
91+
// This view provides a wrapper that applies scene builder operations to the platform view.
92+
// For example, a transform matrix, or setting opacity on the platform view layer.
93+
//
94+
// This is only applies to hybrid composition.
95+
private final SparseArray<FlutterMutatorView> platformViewParent;
8496

8597
// Map of unique IDs to views that render overlay layers.
8698
private final SparseArray<FlutterImageView> overlayLayerViews;
@@ -146,12 +158,17 @@ public void createAndroidViewForPlatformView(
146158
public void disposeAndroidViewForPlatformView(int viewId) {
147159
// Hybrid view.
148160
final View platformView = platformViews.get(viewId);
161+
final FlutterMutatorView parentView = platformViewParent.get(viewId);
149162
if (platformView != null) {
150-
final FlutterMutatorView mutatorView = mutatorViews.get(viewId);
151-
mutatorView.removeView(platformView);
152-
((FlutterView) flutterView).removeView(mutatorView);
163+
if (parentView != null) {
164+
parentView.removeView(platformView);
165+
}
153166
platformViews.remove(viewId);
154-
mutatorViews.remove(viewId);
167+
}
168+
169+
if (parentView != null) {
170+
((FlutterView) flutterView).removeView(parentView);
171+
platformViewParent.remove(viewId);
155172
}
156173
}
157174

@@ -405,7 +422,7 @@ public PlatformViewsController() {
405422
currentFrameUsedPlatformViewIds = new HashSet<>();
406423

407424
platformViews = new SparseArray<>();
408-
mutatorViews = new SparseArray<>();
425+
platformViewParent = new SparseArray<>();
409426

410427
motionEventTracker = MotionEventTracker.getInstance();
411428
}
@@ -681,15 +698,15 @@ void initializePlatformViewIfNeeded(int viewId) {
681698
throw new IllegalStateException(
682699
"Platform view hasn't been initialized from the platform view channel.");
683700
}
684-
if (mutatorViews.get(viewId) != null) {
701+
if (platformViewParent.get(viewId) != null) {
685702
return;
686703
}
687-
final FlutterMutatorView mutatorView =
704+
final FlutterMutatorView parentView =
688705
new FlutterMutatorView(
689706
context, context.getResources().getDisplayMetrics().density, androidTouchProcessor);
690-
mutatorViews.put(viewId, mutatorView);
691-
mutatorView.addView(view);
692-
((FlutterView) flutterView).addView(mutatorView);
707+
platformViewParent.put(viewId, parentView);
708+
parentView.addView(view);
709+
((FlutterView) flutterView).addView(parentView);
693710
}
694711

695712
public void attachToFlutterRenderer(FlutterRenderer flutterRenderer) {
@@ -708,13 +725,14 @@ public void onDisplayPlatformView(
708725
initializeRootImageViewIfNeeded();
709726
initializePlatformViewIfNeeded(viewId);
710727

711-
FlutterMutatorView mutatorView = mutatorViews.get(viewId);
712-
mutatorView.readyToDisplay(mutatorsStack, x, y, width, height);
713-
mutatorView.setVisibility(View.VISIBLE);
714-
mutatorView.bringToFront();
728+
final FlutterMutatorView parentView = platformViewParent.get(viewId);
729+
parentView.readyToDisplay(mutatorsStack, x, y, width, height);
730+
parentView.setVisibility(View.VISIBLE);
731+
parentView.bringToFront();
715732

716-
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(viewWidth, viewHeight);
717-
View platformView = platformViews.get(viewId);
733+
final FrameLayout.LayoutParams layoutParams =
734+
new FrameLayout.LayoutParams(viewWidth, viewHeight);
735+
final View platformView = platformViews.get(viewId);
718736
platformView.setLayoutParams(layoutParams);
719737
platformView.bringToFront();
720738
currentFrameUsedPlatformViewIds.add(viewId);
@@ -723,7 +741,7 @@ public void onDisplayPlatformView(
723741
public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) {
724742
initializeRootImageViewIfNeeded();
725743

726-
FlutterImageView overlayView = overlayLayerViews.get(id);
744+
final FlutterImageView overlayView = overlayLayerViews.get(id);
727745
if (overlayView.getParent() == null) {
728746
((FlutterView) flutterView).addView(overlayView);
729747
}
@@ -766,19 +784,19 @@ public void onEndFrame() {
766784
// If one of the surfaces doesn't have an image, the frame may be incomplete and must be
767785
// dropped.
768786
// For example, a toolbar widget painted by Flutter may not be rendered.
769-
boolean isFrameRenderedUsingImageReaders =
787+
final boolean isFrameRenderedUsingImageReaders =
770788
flutterViewConvertedToImageView && view.acquireLatestImageViewFrame();
771789
finishFrame(isFrameRenderedUsingImageReaders);
772790
}
773791

774792
private void finishFrame(boolean isFrameRenderedUsingImageReaders) {
775793
for (int i = 0; i < overlayLayerViews.size(); i++) {
776-
int overlayId = overlayLayerViews.keyAt(i);
777-
FlutterImageView overlayView = overlayLayerViews.valueAt(i);
794+
final int overlayId = overlayLayerViews.keyAt(i);
795+
final FlutterImageView overlayView = overlayLayerViews.valueAt(i);
778796

779797
if (currentFrameUsedOverlayLayerIds.contains(overlayId)) {
780798
((FlutterView) flutterView).attachOverlaySurfaceToRender(overlayView);
781-
boolean didAcquireOverlaySurfaceImage = overlayView.acquireLatestImage();
799+
final boolean didAcquireOverlaySurfaceImage = overlayView.acquireLatestImage();
782800
isFrameRenderedUsingImageReaders &= didAcquireOverlaySurfaceImage;
783801
} else {
784802
// If the background surface isn't rendered by the image view, then the
@@ -792,22 +810,20 @@ private void finishFrame(boolean isFrameRenderedUsingImageReaders) {
792810
}
793811
}
794812

795-
for (int i = 0; i < platformViews.size(); i++) {
796-
int viewId = platformViews.keyAt(i);
797-
View platformView = platformViews.get(viewId);
798-
View mutatorView = mutatorViews.get(viewId);
813+
for (int i = 0; i < platformViewParent.size(); i++) {
814+
final int viewId = platformViewParent.keyAt(i);
815+
final View parentView = platformViewParent.get(viewId);
799816

800817
// Show platform views only if the surfaces have images available in this frame,
801818
// and if the platform view is rendered in this frame.
819+
// The platform view is appended to a mutator view.
802820
//
803821
// Otherwise, hide the platform view, but don't remove it from the view hierarchy yet as
804822
// they are removed when the framework diposes the platform view widget.
805823
if (isFrameRenderedUsingImageReaders && currentFrameUsedPlatformViewIds.contains(viewId)) {
806-
platformView.setVisibility(View.VISIBLE);
807-
mutatorView.setVisibility(View.VISIBLE);
824+
parentView.setVisibility(View.VISIBLE);
808825
} else {
809-
platformView.setVisibility(View.GONE);
810-
mutatorView.setVisibility(View.GONE);
826+
parentView.setVisibility(View.GONE);
811827
}
812828
}
813829
}

shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,80 @@ public void onEndFrame__destroysOverlaySurfaceAfterFrameOnFlutterSurfaceView() {
445445
verify(overlayImageView, times(1)).detachFromRenderer();
446446
}
447447

448+
@Test
449+
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
450+
public void onEndFrame__removesPlatformView() {
451+
final PlatformViewsController platformViewsController = new PlatformViewsController();
452+
453+
final int platformViewId = 0;
454+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
455+
456+
final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
457+
final PlatformView platformView = mock(PlatformView.class);
458+
final View androidView = mock(View.class);
459+
when(platformView.getView()).thenReturn(androidView);
460+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
461+
462+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
463+
464+
final FlutterJNI jni = new FlutterJNI();
465+
jni.attachToNative(false);
466+
attach(jni, platformViewsController);
467+
468+
jni.onFirstFrame();
469+
470+
// Simulate create call from the framework.
471+
createPlatformView(jni, platformViewsController, platformViewId, "testType");
472+
473+
// Simulate first frame from the framework.
474+
jni.onFirstFrame();
475+
platformViewsController.onBeginFrame();
476+
477+
platformViewsController.onEndFrame();
478+
verify(androidView, never()).setVisibility(View.GONE);
479+
480+
final ViewParent parentView = mock(ViewParent.class);
481+
when(androidView.getParent()).thenReturn(parentView);
482+
}
483+
484+
@Test
485+
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
486+
public void onEndFrame__removesPlatformViewParent() {
487+
final PlatformViewsController platformViewsController = new PlatformViewsController();
488+
489+
final int platformViewId = 0;
490+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
491+
492+
final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
493+
final PlatformView platformView = mock(PlatformView.class);
494+
final View androidView = mock(View.class);
495+
when(platformView.getView()).thenReturn(androidView);
496+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
497+
498+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
499+
500+
final FlutterJNI jni = new FlutterJNI();
501+
jni.attachToNative(false);
502+
503+
final FlutterView flutterView = attach(jni, platformViewsController);
504+
505+
jni.onFirstFrame();
506+
507+
// Simulate create call from the framework.
508+
createPlatformView(jni, platformViewsController, platformViewId, "testType");
509+
platformViewsController.initializePlatformViewIfNeeded(platformViewId);
510+
assertEquals(flutterView.getChildCount(), 2);
511+
512+
// Simulate first frame from the framework.
513+
jni.onFirstFrame();
514+
platformViewsController.onBeginFrame();
515+
platformViewsController.onEndFrame();
516+
517+
// Simulate dispose call from the framework.
518+
disposePlatformView(jni, platformViewsController, platformViewId);
519+
assertEquals(flutterView.getChildCount(), 1);
520+
}
521+
448522
private static byte[] encodeMethodCall(MethodCall call) {
449523
final ByteBuffer buffer = StandardMethodCodec.INSTANCE.encodeMethodCall(call);
450524
buffer.rewind();
@@ -484,7 +558,8 @@ private static void disposePlatformView(
484558
"flutter/platform_views", encodeMethodCall(platformDisposeMethodCall), /*replyId=*/ 0);
485559
}
486560

487-
private static void attach(FlutterJNI jni, PlatformViewsController platformViewsController) {
561+
private static FlutterView attach(
562+
FlutterJNI jni, PlatformViewsController platformViewsController) {
488563
final DartExecutor executor = new DartExecutor(jni, mock(AssetManager.class));
489564
executor.onAttachedToJNI();
490565

@@ -515,6 +590,7 @@ public FlutterImageView createImageView() {
515590

516591
view.attachToFlutterEngine(engine);
517592
platformViewsController.attachToView(view);
593+
return view;
518594
}
519595

520596
@Implements(FlutterJNI.class)

0 commit comments

Comments
 (0)