diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 7c064a6f90818..3c09e57a38d97 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -79,7 +79,6 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega // it is associated with(e.g if a platform view creates other views in the same virtual display. private final HashMap contextToPlatformView; - private final SparseArray platformViewRequests; private final SparseArray platformViews; private final SparseArray mutatorViews; @@ -107,18 +106,45 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega @Override public void createAndroidViewForPlatformView( @NonNull PlatformViewsChannel.PlatformViewCreationRequest request) { - // API level 19 is required for android.graphics.ImageReader. + // API level 19 is required for `android.graphics.ImageReader`. ensureValidAndroidVersion(Build.VERSION_CODES.KITKAT); - platformViewRequests.put(request.viewId, request); + + if (!validateDirection(request.direction)) { + throw new IllegalStateException( + "Trying to create a view with unknown direction value: " + + request.direction + + "(view id: " + + request.viewId + + ")"); + } + + final PlatformViewFactory factory = registry.getFactory(request.viewType); + if (factory == null) { + throw new IllegalStateException( + "Trying to create a platform view of unregistered type: " + request.viewType); + } + + Object createParams = null; + if (request.params != null) { + createParams = factory.getCreateArgsCodec().decodeMessage(request.params); + } + + final PlatformView platformView = factory.create(context, request.viewId, createParams); + final View view = platformView.getView(); + if (view == null) { + throw new IllegalStateException( + "PlatformView#getView() returned null, but an Android view reference was expected."); + } + if (view.getParent() != null) { + throw new IllegalStateException( + "The Android view returned from PlatformView#getView() was already added to a parent view."); + } + platformViews.put(request.viewId, view); } @Override public void disposeAndroidViewForPlatformView(int viewId) { // Hybrid view. - if (platformViewRequests.get(viewId) != null) { - platformViewRequests.remove(viewId); - } - final View platformView = platformViews.get(viewId); if (platformView != null) { final FlutterMutatorView mutatorView = mutatorViews.get(viewId); @@ -378,7 +404,6 @@ public PlatformViewsController() { currentFrameUsedOverlayLayerIds = new HashSet<>(); currentFrameUsedPlatformViewIds = new HashSet<>(); - platformViewRequests = new SparseArray<>(); platformViews = new SparseArray<>(); mutatorViews = new SparseArray<>(); @@ -651,50 +676,15 @@ private void initializeRootImageViewIfNeeded() { @VisibleForTesting void initializePlatformViewIfNeeded(int viewId) { - if (platformViews.get(viewId) != null) { - return; - } - - PlatformViewsChannel.PlatformViewCreationRequest request = platformViewRequests.get(viewId); - if (request == null) { - throw new IllegalStateException( - "Platform view hasn't been initialized from the platform view channel."); - } - - if (!validateDirection(request.direction)) { - throw new IllegalStateException( - "Trying to create a view with unknown direction value: " - + request.direction - + "(view id: " - + viewId - + ")"); - } - - PlatformViewFactory factory = registry.getFactory(request.viewType); - if (factory == null) { - throw new IllegalStateException( - "Trying to create a platform view of unregistered type: " + request.viewType); - } - - Object createParams = null; - if (request.params != null) { - createParams = factory.getCreateArgsCodec().decodeMessage(request.params); - } - - PlatformView platformView = factory.create(context, viewId, createParams); - View view = platformView.getView(); - + final View view = platformViews.get(viewId); if (view == null) { throw new IllegalStateException( - "PlatformView#getView() returned null, but an Android view reference was expected."); + "Platform view hasn't been initialized from the platform view channel."); } - if (view.getParent() != null) { - throw new IllegalStateException( - "The Android view returned from PlatformView#getView() was already added to a parent view."); + if (mutatorViews.get(viewId) != null) { + return; } - platformViews.put(viewId, view); - - FlutterMutatorView mutatorView = + final FlutterMutatorView mutatorView = new FlutterMutatorView( context, context.getResources().getDisplayMetrics().density, androidTouchProcessor); mutatorViews.put(viewId, mutatorView); 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 659056afc5132..3724666b50e48 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.res.AssetManager; +import android.util.SparseArray; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; @@ -28,7 +29,9 @@ import io.flutter.embedding.engine.systemchannels.MouseCursorChannel; import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.embedding.engine.systemchannels.TextInputChannel; +import io.flutter.plugin.common.FlutterException; import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.plugin.common.StandardMethodCodec; import io.flutter.plugin.localization.LocalizationPlugin; import java.nio.ByteBuffer; @@ -242,7 +245,29 @@ public void getPlatformViewById__hybridComposition() { @Test @Config(shadows = {ShadowFlutterJNI.class}) - public void initializePlatformViewIfNeeded__throwsIfViewIsNull() { + public void createPlatformViewMessage__initializesAndroidView() { + PlatformViewsController platformViewsController = new PlatformViewsController(); + + int platformViewId = 0; + assertNull(platformViewsController.getPlatformViewById(platformViewId)); + + PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); + 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); + + FlutterJNI jni = new FlutterJNI(); + attach(jni, platformViewsController); + + // Simulate create call from the framework. + createPlatformView(jni, platformViewsController, platformViewId, "testType"); + verify(platformView, times(1)).getView(); + } + + @Test + @Config(shadows = {ShadowFlutterJNI.class}) + public void createPlatformViewMessage__throwsIfViewIsNull() { PlatformViewsController platformViewsController = new PlatformViewsController(); int platformViewId = 0; @@ -259,22 +284,28 @@ public void initializePlatformViewIfNeeded__throwsIfViewIsNull() { // Simulate create call from the framework. createPlatformView(jni, platformViewsController, platformViewId, "testType"); + assertEquals(ShadowFlutterJNI.getResponses().size(), 1); + final ByteBuffer responseBuffer = ShadowFlutterJNI.getResponses().get(0); + responseBuffer.rewind(); + + StandardMethodCodec methodCodec = new StandardMethodCodec(new StandardMessageCodec()); try { - platformViewsController.initializePlatformViewIfNeeded(platformViewId); - } catch (Exception exception) { - assertTrue(exception instanceof IllegalStateException); - assertEquals( - exception.getMessage(), - "PlatformView#getView() returned null, but an Android view reference was expected."); + methodCodec.decodeEnvelope(responseBuffer); + } catch (FlutterException exception) { + assertTrue( + exception + .getMessage() + .contains( + "PlatformView#getView() returned null, but an Android view reference was expected.")); return; } - assertTrue(false); + assertFalse(true); } @Test @Config(shadows = {ShadowFlutterJNI.class}) - public void initializePlatformViewIfNeeded__throwsIfViewHasParent() { + public void createPlatformViewMessage__throwsIfViewHasParent() { PlatformViewsController platformViewsController = new PlatformViewsController(); int platformViewId = 0; @@ -293,16 +324,23 @@ public void initializePlatformViewIfNeeded__throwsIfViewHasParent() { // Simulate create call from the framework. createPlatformView(jni, platformViewsController, platformViewId, "testType"); + assertEquals(ShadowFlutterJNI.getResponses().size(), 1); + + final ByteBuffer responseBuffer = ShadowFlutterJNI.getResponses().get(0); + responseBuffer.rewind(); + + StandardMethodCodec methodCodec = new StandardMethodCodec(new StandardMessageCodec()); try { - platformViewsController.initializePlatformViewIfNeeded(platformViewId); - } catch (Exception exception) { - assertTrue(exception instanceof IllegalStateException); - assertEquals( - exception.getMessage(), - "The Android view returned from PlatformView#getView() was already added to a parent view."); + methodCodec.decodeEnvelope(responseBuffer); + } catch (FlutterException exception) { + assertTrue( + exception + .getMessage() + .contains( + "The Android view returned from PlatformView#getView() was already added to a parent view.")); return; } - assertTrue(false); + assertFalse(true); } @Test @@ -481,6 +519,7 @@ public FlutterImageView createImageView() { @Implements(FlutterJNI.class) public static class ShadowFlutterJNI { + private static SparseArray replies = new SparseArray<>(); public ShadowFlutterJNI() {} @@ -527,7 +566,13 @@ public void setViewportMetrics( @Implementation public void invokePlatformMessageResponseCallback( - int responseId, ByteBuffer message, int position) {} + int responseId, ByteBuffer message, int position) { + replies.put(responseId, message); + } + + public static SparseArray getResponses() { + return replies; + } } @Implements(SurfaceView.class)