diff --git a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 08c8138655..80e270821e 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -222,7 +222,6 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega layoutParams.topMargin = physicalTop; layoutParams.leftMargin = physicalLeft; wrapperView.setLayoutParams(layoutParams); - wrapperView.setLayoutDirection(request.direction); final View view = platformView.getView(); if (view == null) { @@ -232,6 +231,8 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega throw new IllegalStateException( "The Android view returned from PlatformView#getView() was already added to a parent view."); } + view.setLayoutParams(new FrameLayout.LayoutParams(physicalWidth, physicalHeight)); + view.setLayoutDirection(request.direction); wrapperView.addView(view); wrapperView.setOnDescendantFocusChangeListener( (v, hasFocus) -> { @@ -301,8 +302,9 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega public PlatformViewsChannel.PlatformViewBufferSize resize( @NonNull PlatformViewsChannel.PlatformViewResizeRequest request) { final int viewId = request.viewId; + final PlatformView platformView = platformViews.get(viewId); final PlatformViewWrapper view = viewWrappers.get(viewId); - if (view == null) { + if (platformView == null || view == null) { Log.e(TAG, "Resizing unknown platform view with id: " + viewId); return null; } @@ -321,12 +323,18 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega view.setBufferSize(newWidth, newHeight); } - final FrameLayout.LayoutParams layoutParams = - (FrameLayout.LayoutParams) view.getLayoutParams(); - layoutParams.width = newWidth; - layoutParams.height = newHeight; - view.setLayoutParams(layoutParams); + final ViewGroup.LayoutParams viewWrapperLayoutParams = view.getLayoutParams(); + viewWrapperLayoutParams.width = newWidth; + viewWrapperLayoutParams.height = newHeight; + view.setLayoutParams(viewWrapperLayoutParams); + final View embeddedView = platformView.getView(); + if (embeddedView != null) { + final ViewGroup.LayoutParams embeddedViewLayoutParams = embeddedView.getLayoutParams(); + embeddedViewLayoutParams.width = newWidth; + embeddedViewLayoutParams.height = newHeight; + embeddedView.setLayoutParams(embeddedViewLayoutParams); + } return new PlatformViewsChannel.PlatformViewBufferSize( toLogicalPixels(view.getBufferWidth()), toLogicalPixels(view.getBufferHeight())); } diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 1d3cd0e0c6..0b6e4b8edd 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -17,6 +17,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewParent; +import android.widget.FrameLayout; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import io.flutter.embedding.android.FlutterImageView; @@ -41,10 +42,12 @@ import io.flutter.view.TextureRegistry; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -149,6 +152,65 @@ public class PlatformViewsControllerTest { verify(viewFactory, times(1)).create(any(), eq(platformViewId), any()); } + @Test + @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + public void createPlatformViewMessage__setsAndroidViewLayoutDirection() { + PlatformViewsController platformViewsController = new PlatformViewsController(); + platformViewsController.setSoftwareRendering(true); + + int platformViewId = 0; + assertNull(platformViewsController.getPlatformViewById(platformViewId)); + + PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); + PlatformView platformView = mock(PlatformView.class); + + View androidView = mock(View.class); + when(platformView.getView()).thenReturn(androidView); + 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", /* hybrid=*/ false); + verify(androidView, times(1)).setLayoutDirection(0); + } + + @Test + @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + public void createPlatformViewMessage__setsAndroidViewSize() { + PlatformViewsController platformViewsController = new PlatformViewsController(); + platformViewsController.setSoftwareRendering(true); + + int platformViewId = 0; + assertNull(platformViewsController.getPlatformViewById(platformViewId)); + + PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); + PlatformView platformView = mock(PlatformView.class); + + View androidView = mock(View.class); + when(platformView.getView()).thenReturn(androidView); + 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", /* hybrid=*/ false); + + ArgumentCaptor layoutParamsCaptor = + ArgumentCaptor.forClass(FrameLayout.LayoutParams.class); + verify(androidView, times(2)).setLayoutParams(layoutParamsCaptor.capture()); + + List capturedLayoutParams = layoutParamsCaptor.getAllValues(); + assertEquals(capturedLayoutParams.get(0).width, 1); + assertEquals(capturedLayoutParams.get(0).height, 1); + } + @Test @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) public void createPlatformViewMessage__throwsIfViewIsNull() { @@ -301,6 +363,43 @@ public class PlatformViewsControllerTest { assertEquals(ShadowFlutterJNI.getResponses().get(0).limit(), 2); } + @Test + @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + public void resizeAndroidView() { + PlatformViewsController platformViewsController = new PlatformViewsController(); + platformViewsController.setSoftwareRendering(true); + + int platformViewId = 0; + assertNull(platformViewsController.getPlatformViewById(platformViewId)); + + PlatformViewFactory viewFactory = mock(PlatformViewFactory.class); + PlatformView platformView = mock(PlatformView.class); + final View androidView = mock(View.class); + when(platformView.getView()).thenReturn(androidView); + 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", /* hybrid=*/ false); + + reset(androidView); + when(androidView.getLayoutParams()).thenReturn(new FrameLayout.LayoutParams(0, 0)); + + // Simulate a resize call from the framework. + resize(jni, platformViewsController, platformViewId, 10.0, 20.0); + + ArgumentCaptor layoutParamsCaptor = + ArgumentCaptor.forClass(FrameLayout.LayoutParams.class); + verify(androidView, times(1)).setLayoutParams(layoutParamsCaptor.capture()); + + assertEquals(layoutParamsCaptor.getValue().width, 10); + assertEquals(layoutParamsCaptor.getValue().height, 20); + } + @Test @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) public void disposeAndroidView__hybridComposition() { @@ -822,16 +921,15 @@ public class PlatformViewsControllerTest { int platformViewId, String viewType, boolean hybrid) { - final Map platformViewCreateArguments = new HashMap<>(); - platformViewCreateArguments.put("hybrid", hybrid); - platformViewCreateArguments.put("id", platformViewId); - platformViewCreateArguments.put("viewType", viewType); - platformViewCreateArguments.put("direction", 0); - platformViewCreateArguments.put("width", 1.0); - platformViewCreateArguments.put("height", 1.0); + final Map args = new HashMap<>(); + args.put("hybrid", hybrid); + args.put("id", platformViewId); + args.put("viewType", viewType); + args.put("direction", 0); + args.put("width", 1.0); + args.put("height", 1.0); - final MethodCall platformCreateMethodCall = - new MethodCall("create", platformViewCreateArguments); + final MethodCall platformCreateMethodCall = new MethodCall("create", args); jni.handlePlatformMessage( "flutter/platform_views", @@ -845,16 +943,35 @@ public class PlatformViewsControllerTest { PlatformViewsController platformViewsController, int platformViewId, int direction) { - final Map platformViewCreateArguments = new HashMap<>(); - platformViewCreateArguments.put("id", platformViewId); - platformViewCreateArguments.put("direction", direction); + final Map args = new HashMap<>(); + args.put("id", platformViewId); + args.put("direction", direction); - final MethodCall platformCreateMethodCall = - new MethodCall("setDirection", platformViewCreateArguments); + final MethodCall platformSetDirectionMethodCall = new MethodCall("setDirection", args); jni.handlePlatformMessage( "flutter/platform_views", - encodeMethodCall(platformCreateMethodCall), + encodeMethodCall(platformSetDirectionMethodCall), + /*replyId=*/ 0, + /*messageData=*/ 0); + } + + private static void resize( + FlutterJNI jni, + PlatformViewsController platformViewsController, + int platformViewId, + double width, + double height) { + final Map args = new HashMap<>(); + args.put("id", platformViewId); + args.put("width", width); + args.put("height", height); + + final MethodCall platformResizeMethodCall = new MethodCall("resize", args); + + jni.handlePlatformMessage( + "flutter/platform_views", + encodeMethodCall(platformResizeMethodCall), /*replyId=*/ 0, /*messageData=*/ 0); } @@ -862,12 +979,11 @@ public class PlatformViewsControllerTest { private static void disposePlatformView( FlutterJNI jni, PlatformViewsController platformViewsController, int platformViewId) { - final Map platformViewDisposeArguments = new HashMap<>(); - platformViewDisposeArguments.put("hybrid", true); - platformViewDisposeArguments.put("id", platformViewId); + final Map args = new HashMap<>(); + args.put("hybrid", true); + args.put("id", platformViewId); - final MethodCall platformDisposeMethodCall = - new MethodCall("dispose", platformViewDisposeArguments); + final MethodCall platformDisposeMethodCall = new MethodCall("dispose", args); jni.handlePlatformMessage( "flutter/platform_views",