Skip to content

Commit 9333b7c

Browse files
author
Emmanuel Garcia
authored
Create PlatformView instance right after method channel call from Dart (flutter#20500)
1 parent 429675d commit 9333b7c

File tree

2 files changed

+100
-65
lines changed

2 files changed

+100
-65
lines changed

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

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ 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;
8382
private final SparseArray<View> platformViews;
8483
private final SparseArray<FlutterMutatorView> mutatorViews;
8584

@@ -107,18 +106,45 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
107106
@Override
108107
public void createAndroidViewForPlatformView(
109108
@NonNull PlatformViewsChannel.PlatformViewCreationRequest request) {
110-
// API level 19 is required for android.graphics.ImageReader.
109+
// API level 19 is required for `android.graphics.ImageReader`.
111110
ensureValidAndroidVersion(Build.VERSION_CODES.KITKAT);
112-
platformViewRequests.put(request.viewId, request);
111+
112+
if (!validateDirection(request.direction)) {
113+
throw new IllegalStateException(
114+
"Trying to create a view with unknown direction value: "
115+
+ request.direction
116+
+ "(view id: "
117+
+ request.viewId
118+
+ ")");
119+
}
120+
121+
final PlatformViewFactory factory = registry.getFactory(request.viewType);
122+
if (factory == null) {
123+
throw new IllegalStateException(
124+
"Trying to create a platform view of unregistered type: " + request.viewType);
125+
}
126+
127+
Object createParams = null;
128+
if (request.params != null) {
129+
createParams = factory.getCreateArgsCodec().decodeMessage(request.params);
130+
}
131+
132+
final PlatformView platformView = factory.create(context, request.viewId, createParams);
133+
final View view = platformView.getView();
134+
if (view == null) {
135+
throw new IllegalStateException(
136+
"PlatformView#getView() returned null, but an Android view reference was expected.");
137+
}
138+
if (view.getParent() != null) {
139+
throw new IllegalStateException(
140+
"The Android view returned from PlatformView#getView() was already added to a parent view.");
141+
}
142+
platformViews.put(request.viewId, view);
113143
}
114144

115145
@Override
116146
public void disposeAndroidViewForPlatformView(int viewId) {
117147
// Hybrid view.
118-
if (platformViewRequests.get(viewId) != null) {
119-
platformViewRequests.remove(viewId);
120-
}
121-
122148
final View platformView = platformViews.get(viewId);
123149
if (platformView != null) {
124150
final FlutterMutatorView mutatorView = mutatorViews.get(viewId);
@@ -378,7 +404,6 @@ public PlatformViewsController() {
378404
currentFrameUsedOverlayLayerIds = new HashSet<>();
379405
currentFrameUsedPlatformViewIds = new HashSet<>();
380406

381-
platformViewRequests = new SparseArray<>();
382407
platformViews = new SparseArray<>();
383408
mutatorViews = new SparseArray<>();
384409

@@ -651,50 +676,15 @@ private void initializeRootImageViewIfNeeded() {
651676

652677
@VisibleForTesting
653678
void initializePlatformViewIfNeeded(int viewId) {
654-
if (platformViews.get(viewId) != null) {
655-
return;
656-
}
657-
658-
PlatformViewsChannel.PlatformViewCreationRequest request = platformViewRequests.get(viewId);
659-
if (request == null) {
660-
throw new IllegalStateException(
661-
"Platform view hasn't been initialized from the platform view channel.");
662-
}
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-
679+
final View view = platformViews.get(viewId);
687680
if (view == null) {
688681
throw new IllegalStateException(
689-
"PlatformView#getView() returned null, but an Android view reference was expected.");
682+
"Platform view hasn't been initialized from the platform view channel.");
690683
}
691-
if (view.getParent() != null) {
692-
throw new IllegalStateException(
693-
"The Android view returned from PlatformView#getView() was already added to a parent view.");
684+
if (mutatorViews.get(viewId) != null) {
685+
return;
694686
}
695-
platformViews.put(viewId, view);
696-
697-
FlutterMutatorView mutatorView =
687+
final FlutterMutatorView mutatorView =
698688
new FlutterMutatorView(
699689
context, context.getResources().getDisplayMetrics().density, androidTouchProcessor);
700690
mutatorViews.put(viewId, mutatorView);

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

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import android.content.Context;
99
import android.content.res.AssetManager;
10+
import android.util.SparseArray;
1011
import android.view.MotionEvent;
1112
import android.view.Surface;
1213
import android.view.SurfaceHolder;
@@ -28,7 +29,9 @@
2829
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
2930
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
3031
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
32+
import io.flutter.plugin.common.FlutterException;
3133
import io.flutter.plugin.common.MethodCall;
34+
import io.flutter.plugin.common.StandardMessageCodec;
3235
import io.flutter.plugin.common.StandardMethodCodec;
3336
import io.flutter.plugin.localization.LocalizationPlugin;
3437
import java.nio.ByteBuffer;
@@ -242,7 +245,29 @@ public void getPlatformViewById__hybridComposition() {
242245

243246
@Test
244247
@Config(shadows = {ShadowFlutterJNI.class})
245-
public void initializePlatformViewIfNeeded__throwsIfViewIsNull() {
248+
public void createPlatformViewMessage__initializesAndroidView() {
249+
PlatformViewsController platformViewsController = new PlatformViewsController();
250+
251+
int platformViewId = 0;
252+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
253+
254+
PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
255+
PlatformView platformView = mock(PlatformView.class);
256+
when(platformView.getView()).thenReturn(mock(View.class));
257+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
258+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
259+
260+
FlutterJNI jni = new FlutterJNI();
261+
attach(jni, platformViewsController);
262+
263+
// Simulate create call from the framework.
264+
createPlatformView(jni, platformViewsController, platformViewId, "testType");
265+
verify(platformView, times(1)).getView();
266+
}
267+
268+
@Test
269+
@Config(shadows = {ShadowFlutterJNI.class})
270+
public void createPlatformViewMessage__throwsIfViewIsNull() {
246271
PlatformViewsController platformViewsController = new PlatformViewsController();
247272

248273
int platformViewId = 0;
@@ -259,22 +284,28 @@ public void initializePlatformViewIfNeeded__throwsIfViewIsNull() {
259284

260285
// Simulate create call from the framework.
261286
createPlatformView(jni, platformViewsController, platformViewId, "testType");
287+
assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
262288

289+
final ByteBuffer responseBuffer = ShadowFlutterJNI.getResponses().get(0);
290+
responseBuffer.rewind();
291+
292+
StandardMethodCodec methodCodec = new StandardMethodCodec(new StandardMessageCodec());
263293
try {
264-
platformViewsController.initializePlatformViewIfNeeded(platformViewId);
265-
} catch (Exception exception) {
266-
assertTrue(exception instanceof IllegalStateException);
267-
assertEquals(
268-
exception.getMessage(),
269-
"PlatformView#getView() returned null, but an Android view reference was expected.");
294+
methodCodec.decodeEnvelope(responseBuffer);
295+
} catch (FlutterException exception) {
296+
assertTrue(
297+
exception
298+
.getMessage()
299+
.contains(
300+
"PlatformView#getView() returned null, but an Android view reference was expected."));
270301
return;
271302
}
272-
assertTrue(false);
303+
assertFalse(true);
273304
}
274305

275306
@Test
276307
@Config(shadows = {ShadowFlutterJNI.class})
277-
public void initializePlatformViewIfNeeded__throwsIfViewHasParent() {
308+
public void createPlatformViewMessage__throwsIfViewHasParent() {
278309
PlatformViewsController platformViewsController = new PlatformViewsController();
279310

280311
int platformViewId = 0;
@@ -293,16 +324,23 @@ public void initializePlatformViewIfNeeded__throwsIfViewHasParent() {
293324

294325
// Simulate create call from the framework.
295326
createPlatformView(jni, platformViewsController, platformViewId, "testType");
327+
assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
328+
329+
final ByteBuffer responseBuffer = ShadowFlutterJNI.getResponses().get(0);
330+
responseBuffer.rewind();
331+
332+
StandardMethodCodec methodCodec = new StandardMethodCodec(new StandardMessageCodec());
296333
try {
297-
platformViewsController.initializePlatformViewIfNeeded(platformViewId);
298-
} catch (Exception exception) {
299-
assertTrue(exception instanceof IllegalStateException);
300-
assertEquals(
301-
exception.getMessage(),
302-
"The Android view returned from PlatformView#getView() was already added to a parent view.");
334+
methodCodec.decodeEnvelope(responseBuffer);
335+
} catch (FlutterException exception) {
336+
assertTrue(
337+
exception
338+
.getMessage()
339+
.contains(
340+
"The Android view returned from PlatformView#getView() was already added to a parent view."));
303341
return;
304342
}
305-
assertTrue(false);
343+
assertFalse(true);
306344
}
307345

308346
@Test
@@ -481,6 +519,7 @@ public FlutterImageView createImageView() {
481519

482520
@Implements(FlutterJNI.class)
483521
public static class ShadowFlutterJNI {
522+
private static SparseArray<ByteBuffer> replies = new SparseArray<>();
484523

485524
public ShadowFlutterJNI() {}
486525

@@ -527,7 +566,13 @@ public void setViewportMetrics(
527566

528567
@Implementation
529568
public void invokePlatformMessageResponseCallback(
530-
int responseId, ByteBuffer message, int position) {}
569+
int responseId, ByteBuffer message, int position) {
570+
replies.put(responseId, message);
571+
}
572+
573+
public static SparseArray<ByteBuffer> getResponses() {
574+
return replies;
575+
}
531576
}
532577

533578
@Implements(SurfaceView.class)

0 commit comments

Comments
 (0)