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

Commit 1f15b7e

Browse files
author
Emmanuel Garcia
committed
Fix crash
1 parent d010396 commit 1f15b7e

2 files changed

Lines changed: 124 additions & 65 deletions

File tree

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

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +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-
private final SparseArray<PlatformViewsChannel.PlatformViewCreationRequest> platformViewRequests;
82+
// The view returned by `PlatformView#getView()`.
83+
//
84+
// This only applies to hybrid composition.
8385
private final SparseArray<View> platformViews;
84-
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;
8596

8697
// Map of unique IDs to views that render overlay layers.
8798
private final SparseArray<FlutterImageView> overlayLayerViews;
@@ -120,12 +131,17 @@ public void disposeAndroidViewForPlatformView(int viewId) {
120131
}
121132

122133
final View platformView = platformViews.get(viewId);
134+
final FlutterMutatorView parentView = platformViewParent.get(viewId);
123135
if (platformView != null) {
124-
final FlutterMutatorView mutatorView = mutatorViews.get(viewId);
125-
mutatorView.removeView(platformView);
126-
((FlutterView) flutterView).removeView(mutatorView);
136+
if (parentView != null) {
137+
parentView.removeView(platformView);
138+
}
127139
platformViews.remove(viewId);
128-
mutatorViews.remove(viewId);
140+
}
141+
142+
if (parentView != null) {
143+
((FlutterView) flutterView).removeView(parentView);
144+
platformViewParent.remove(viewId);
129145
}
130146
}
131147

@@ -380,7 +396,7 @@ public PlatformViewsController() {
380396

381397
platformViewRequests = new SparseArray<>();
382398
platformViews = new SparseArray<>();
383-
mutatorViews = new SparseArray<>();
399+
platformViewParent = new SparseArray<>();
384400

385401
motionEventTracker = MotionEventTracker.getInstance();
386402
}
@@ -654,52 +670,20 @@ void initializePlatformViewIfNeeded(int viewId) {
654670
if (platformViews.get(viewId) != null) {
655671
return;
656672
}
657-
658673
PlatformViewsChannel.PlatformViewCreationRequest request = platformViewRequests.get(viewId);
659674
if (request == null) {
660675
throw new IllegalStateException(
661676
"Platform view hasn't been initialized from the platform view channel.");
662677
}
663-
664-
if (!validateDirection(request.direction)) {
665-
throw new IllegalStateException(
666-
"Trying to create a view with unknown direction value: "
667-
+ request.direction
668-
+ "(view id: "
669-
+ viewId
670-
+ ")");
671-
}
672-
673-
PlatformViewFactory factory = registry.getFactory(request.viewType);
674-
if (factory == null) {
675-
throw new IllegalStateException(
676-
"Trying to create a platform view of unregistered type: " + request.viewType);
677-
}
678-
679-
Object createParams = null;
680-
if (request.params != null) {
681-
createParams = factory.getCreateArgsCodec().decodeMessage(request.params);
682-
}
683-
684-
PlatformView platformView = factory.create(context, viewId, createParams);
685-
View view = platformView.getView();
686-
687-
if (view == null) {
688-
throw new IllegalStateException(
689-
"PlatformView#getView() returned null, but an Android view reference was expected.");
690-
}
691-
if (view.getParent() != null) {
692-
throw new IllegalStateException(
693-
"The Android view returned from PlatformView#getView() was already added to a parent view.");
678+
if (platformViewParent.get(viewId) != null) {
679+
return;
694680
}
695-
platformViews.put(viewId, view);
696-
697-
FlutterMutatorView mutatorView =
681+
final FlutterMutatorView parentView =
698682
new FlutterMutatorView(
699683
context, context.getResources().getDisplayMetrics().density, androidTouchProcessor);
700-
mutatorViews.put(viewId, mutatorView);
701-
mutatorView.addView(view);
702-
((FlutterView) flutterView).addView(mutatorView);
684+
platformViewParent.put(viewId, parentView);
685+
parentView.addView(view);
686+
((FlutterView) flutterView).addView(parentView);
703687
}
704688

705689
public void attachToFlutterRenderer(FlutterRenderer flutterRenderer) {
@@ -718,13 +702,14 @@ public void onDisplayPlatformView(
718702
initializeRootImageViewIfNeeded();
719703
initializePlatformViewIfNeeded(viewId);
720704

721-
FlutterMutatorView mutatorView = mutatorViews.get(viewId);
722-
mutatorView.readyToDisplay(mutatorsStack, x, y, width, height);
723-
mutatorView.setVisibility(View.VISIBLE);
724-
mutatorView.bringToFront();
705+
final FlutterMutatorView parentView = platformViewParent.get(viewId);
706+
parentView.readyToDisplay(mutatorsStack, x, y, width, height);
707+
parentView.setVisibility(View.VISIBLE);
708+
parentView.bringToFront();
725709

726-
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(viewWidth, viewHeight);
727-
View platformView = platformViews.get(viewId);
710+
final FrameLayout.LayoutParams layoutParams =
711+
new FrameLayout.LayoutParams(viewWidth, viewHeight);
712+
final View platformView = platformViews.get(viewId);
728713
platformView.setLayoutParams(layoutParams);
729714
platformView.bringToFront();
730715
currentFrameUsedPlatformViewIds.add(viewId);
@@ -733,7 +718,7 @@ public void onDisplayPlatformView(
733718
public void onDisplayOverlaySurface(int id, int x, int y, int width, int height) {
734719
initializeRootImageViewIfNeeded();
735720

736-
FlutterImageView overlayView = overlayLayerViews.get(id);
721+
final FlutterImageView overlayView = overlayLayerViews.get(id);
737722
if (overlayView.getParent() == null) {
738723
((FlutterView) flutterView).addView(overlayView);
739724
}
@@ -776,19 +761,19 @@ public void onEndFrame() {
776761
// If one of the surfaces doesn't have an image, the frame may be incomplete and must be
777762
// dropped.
778763
// For example, a toolbar widget painted by Flutter may not be rendered.
779-
boolean isFrameRenderedUsingImageReaders =
764+
final boolean isFrameRenderedUsingImageReaders =
780765
flutterViewConvertedToImageView && view.acquireLatestImageViewFrame();
781766
finishFrame(isFrameRenderedUsingImageReaders);
782767
}
783768

784769
private void finishFrame(boolean isFrameRenderedUsingImageReaders) {
785770
for (int i = 0; i < overlayLayerViews.size(); i++) {
786-
int overlayId = overlayLayerViews.keyAt(i);
787-
FlutterImageView overlayView = overlayLayerViews.valueAt(i);
771+
final int overlayId = overlayLayerViews.keyAt(i);
772+
final FlutterImageView overlayView = overlayLayerViews.valueAt(i);
788773

789774
if (currentFrameUsedOverlayLayerIds.contains(overlayId)) {
790775
((FlutterView) flutterView).attachOverlaySurfaceToRender(overlayView);
791-
boolean didAcquireOverlaySurfaceImage = overlayView.acquireLatestImage();
776+
final boolean didAcquireOverlaySurfaceImage = overlayView.acquireLatestImage();
792777
isFrameRenderedUsingImageReaders &= didAcquireOverlaySurfaceImage;
793778
} else {
794779
// If the background surface isn't rendered by the image view, then the
@@ -802,22 +787,20 @@ private void finishFrame(boolean isFrameRenderedUsingImageReaders) {
802787
}
803788
}
804789

805-
for (int i = 0; i < platformViews.size(); i++) {
806-
int viewId = platformViews.keyAt(i);
807-
View platformView = platformViews.get(viewId);
808-
View mutatorView = mutatorViews.get(viewId);
790+
for (int i = 0; i < platformViewParent.size(); i++) {
791+
final int viewId = platformViewParent.keyAt(i);
792+
final View parentView = platformViewParent.get(viewId);
809793

810794
// Show platform views only if the surfaces have images available in this frame,
811795
// and if the platform view is rendered in this frame.
796+
// The platform view is appended to a mutator view.
812797
//
813798
// Otherwise, hide the platform view, but don't remove it from the view hierarchy yet as
814799
// they are removed when the framework diposes the platform view widget.
815800
if (isFrameRenderedUsingImageReaders && currentFrameUsedPlatformViewIds.contains(viewId)) {
816-
platformView.setVisibility(View.VISIBLE);
817-
mutatorView.setVisibility(View.VISIBLE);
801+
parentView.setVisibility(View.VISIBLE);
818802
} else {
819-
platformView.setVisibility(View.GONE);
820-
mutatorView.setVisibility(View.GONE);
803+
parentView.setVisibility(View.GONE);
821804
}
822805
}
823806
}

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

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

410+
@Test
411+
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
412+
public void onEndFrame__removesPlatformView() {
413+
final PlatformViewsController platformViewsController = new PlatformViewsController();
414+
415+
final int platformViewId = 0;
416+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
417+
418+
final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
419+
final PlatformView platformView = mock(PlatformView.class);
420+
final View androidView = mock(View.class);
421+
when(platformView.getView()).thenReturn(androidView);
422+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
423+
424+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
425+
426+
final FlutterJNI jni = new FlutterJNI();
427+
jni.attachToNative(false);
428+
attach(jni, platformViewsController);
429+
430+
jni.onFirstFrame();
431+
432+
// Simulate create call from the framework.
433+
createPlatformView(jni, platformViewsController, platformViewId, "testType");
434+
435+
// Simulate first frame from the framework.
436+
jni.onFirstFrame();
437+
platformViewsController.onBeginFrame();
438+
439+
platformViewsController.onEndFrame();
440+
verify(androidView, never()).setVisibility(View.GONE);
441+
442+
final ViewParent parentView = mock(ViewParent.class);
443+
when(androidView.getParent()).thenReturn(parentView);
444+
}
445+
446+
@Test
447+
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
448+
public void onEndFrame__removesPlatformViewParent() {
449+
final PlatformViewsController platformViewsController = new PlatformViewsController();
450+
451+
final int platformViewId = 0;
452+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
453+
454+
final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
455+
final PlatformView platformView = mock(PlatformView.class);
456+
final View androidView = mock(View.class);
457+
when(platformView.getView()).thenReturn(androidView);
458+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
459+
460+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
461+
462+
final FlutterJNI jni = new FlutterJNI();
463+
jni.attachToNative(false);
464+
465+
final FlutterView flutterView = attach(jni, platformViewsController);
466+
467+
jni.onFirstFrame();
468+
469+
// Simulate create call from the framework.
470+
createPlatformView(jni, platformViewsController, platformViewId, "testType");
471+
platformViewsController.initializePlatformViewIfNeeded(platformViewId);
472+
assertEquals(flutterView.getChildCount(), 2);
473+
474+
// Simulate first frame from the framework.
475+
jni.onFirstFrame();
476+
platformViewsController.onBeginFrame();
477+
platformViewsController.onEndFrame();
478+
479+
// Simulate dispose call from the framework.
480+
disposePlatformView(jni, platformViewsController, platformViewId);
481+
assertEquals(flutterView.getChildCount(), 1);
482+
}
483+
410484
private static byte[] encodeMethodCall(MethodCall call) {
411485
final ByteBuffer buffer = StandardMethodCodec.INSTANCE.encodeMethodCall(call);
412486
buffer.rewind();
@@ -446,7 +520,8 @@ private static void disposePlatformView(
446520
"flutter/platform_views", encodeMethodCall(platformDisposeMethodCall), /*replyId=*/ 0);
447521
}
448522

449-
private static void attach(FlutterJNI jni, PlatformViewsController platformViewsController) {
523+
private static FlutterView attach(
524+
FlutterJNI jni, PlatformViewsController platformViewsController) {
450525
final DartExecutor executor = new DartExecutor(jni, mock(AssetManager.class));
451526
executor.onAttachedToJNI();
452527

@@ -477,6 +552,7 @@ public FlutterImageView createImageView() {
477552

478553
view.attachToFlutterEngine(engine);
479554
platformViewsController.attachToView(view);
555+
return view;
480556
}
481557

482558
@Implements(FlutterJNI.class)

0 commit comments

Comments
 (0)