Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
PlatformViewsControllerTest.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.plugin.platform;
6
7import static android.os.Looper.getMainLooper;
8import static io.flutter.embedding.engine.systemchannels.PlatformViewsChannel.PlatformViewTouch;
9import static org.junit.Assert.*;
10import static org.mockito.ArgumentMatchers.*;
11import static org.mockito.Mockito.*;
12import static org.robolectric.Shadows.shadowOf;
13
14import android.app.Presentation;
15import android.content.Context;
16import android.content.MutableContextWrapper;
17import android.content.res.AssetManager;
18import android.graphics.SurfaceTexture;
19import android.media.Image;
20import android.util.SparseArray;
21import android.view.MotionEvent;
22import android.view.Surface;
23import android.view.SurfaceHolder;
24import android.view.SurfaceView;
25import android.view.View;
26import android.view.ViewParent;
27import android.widget.FrameLayout;
28import android.widget.FrameLayout.LayoutParams;
29import androidx.annotation.NonNull;
30import androidx.test.core.app.ApplicationProvider;
31import androidx.test.ext.junit.runners.AndroidJUnit4;
32import io.flutter.embedding.android.FlutterImageView;
33import io.flutter.embedding.android.FlutterSurfaceView;
34import io.flutter.embedding.android.FlutterView;
35import io.flutter.embedding.android.MotionEventTracker;
36import io.flutter.embedding.engine.FlutterEngine;
37import io.flutter.embedding.engine.FlutterJNI;
38import io.flutter.embedding.engine.FlutterOverlaySurface;
39import io.flutter.embedding.engine.dart.DartExecutor;
40import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorView;
41import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack;
42import io.flutter.embedding.engine.renderer.FlutterRenderer;
43import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
44import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
45import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
46import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel.PlatformViewTouch;
47import io.flutter.embedding.engine.systemchannels.SettingsChannel;
48import io.flutter.embedding.engine.systemchannels.TextInputChannel;
49import io.flutter.plugin.common.MethodCall;
50import io.flutter.plugin.common.StandardMessageCodec;
51import io.flutter.plugin.common.StandardMethodCodec;
52import io.flutter.plugin.localization.LocalizationPlugin;
53import io.flutter.view.TextureRegistry;
54import java.nio.ByteBuffer;
55import java.util.Arrays;
56import java.util.HashMap;
57import java.util.List;
58import java.util.Map;
59import org.junit.Ignore;
60import org.junit.Test;
61import org.junit.runner.RunWith;
62import org.mockito.ArgumentCaptor;
63import org.robolectric.annotation.Config;
64import org.robolectric.annotation.Implementation;
65import org.robolectric.annotation.Implements;
66import org.robolectric.shadows.ShadowDialog;
67import org.robolectric.shadows.ShadowSurfaceView;
68
69@Config(manifest = Config.NONE)
70@RunWith(AndroidJUnit4.class)
72 // An implementation of PlatformView that counts invocations of its lifecycle callbacks.
74 static final String VIEW_TYPE_ID = "CountingPlatformView";
75 private View view;
76
77 public CountingPlatformView(Context context) {
78 view = new SurfaceView(context);
79 }
80
81 public int disposeCalls = 0;
82 public int attachCalls = 0;
83 public int detachCalls = 0;
84
85 @Override
86 public void dispose() {
87 // We have been removed from the view hierarhy before the call to dispose.
88 assertNull(view.getParent());
89 disposeCalls++;
90 }
91
92 @Override
93 public View getView() {
94 return view;
95 }
96
97 @Override
98 public void onFlutterViewAttached(View flutterView) {
99 attachCalls++;
100 }
101
102 @Override
103 public void onFlutterViewDetached() {
104 detachCalls++;
105 }
106 }
107
108 @Test
109 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
111 PlatformViewsController platformViewsController = new PlatformViewsController();
112 FlutterJNI jni = new FlutterJNI();
113 attach(jni, platformViewsController);
114 // Get the platform view registry.
115 PlatformViewRegistry registry = platformViewsController.getRegistry();
116
117 // Register a factory for our platform view.
118 registry.registerViewFactory(
120 new PlatformViewFactory(StandardMessageCodec.INSTANCE) {
121 @Override
122 public PlatformView create(Context context, int viewId, Object args) {
123 return new CountingPlatformView(context);
124 }
125 });
126
127 // Create the platform view.
128 int viewId = 0;
129 final PlatformViewsChannel.PlatformViewCreationRequest request =
130 new PlatformViewsChannel.PlatformViewCreationRequest(
131 viewId,
132 CountingPlatformView.VIEW_TYPE_ID,
133 0,
134 0,
135 128,
136 128,
137 View.LAYOUT_DIRECTION_LTR,
138 null);
139 PlatformView pView = platformViewsController.createPlatformView(request, true);
140 assertTrue(pView instanceof CountingPlatformView);
141 CountingPlatformView cpv = (CountingPlatformView) pView;
142 platformViewsController.configureForTextureLayerComposition(pView, request);
143 assertEquals(0, cpv.disposeCalls);
144 platformViewsController.disposePlatformView(viewId);
145 assertEquals(1, cpv.disposeCalls);
146 }
147
148 @Test
149 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
151 PlatformViewsController platformViewsController = new PlatformViewsController();
152 FlutterJNI jni = new FlutterJNI();
153 attach(jni, platformViewsController);
154 // Get the platform view registry.
155 PlatformViewRegistry registry = platformViewsController.getRegistry();
156
157 // Register a factory for our platform view.
158 registry.registerViewFactory(
160 new PlatformViewFactory(StandardMessageCodec.INSTANCE) {
161 @Override
162 public PlatformView create(Context context, int viewId, Object args) {
163 return new CountingPlatformView(context);
164 }
165 });
166
167 // Create the platform view.
168 int viewId = 0;
169 final PlatformViewsChannel.PlatformViewCreationRequest request =
170 new PlatformViewsChannel.PlatformViewCreationRequest(
171 viewId,
172 CountingPlatformView.VIEW_TYPE_ID,
173 0,
174 0,
175 128,
176 128,
177 View.LAYOUT_DIRECTION_LTR,
178 null);
179 PlatformView pView = platformViewsController.createPlatformView(request, true);
180 assertTrue(pView instanceof CountingPlatformView);
181 CountingPlatformView cpv = (CountingPlatformView) pView;
182 assertEquals(1, cpv.attachCalls);
183 assertEquals(0, cpv.detachCalls);
184 assertEquals(0, cpv.disposeCalls);
185 platformViewsController.detachFromView();
186 assertEquals(1, cpv.attachCalls);
187 assertEquals(1, cpv.detachCalls);
188 assertEquals(0, cpv.disposeCalls);
189 platformViewsController.disposePlatformView(viewId);
190 }
191
192 @Ignore
193 @Test
195 // Setup test structure.
196 // Create a fake View that represents the View that renders a Flutter UI.
197 View fakeFlutterView = new View(ApplicationProvider.getApplicationContext());
198
199 // Create fake VirtualDisplayControllers. This requires internal knowledge of
200 // PlatformViewsController. We know that all PlatformViewsController does is
201 // forward view attachment/detachment calls to it's VirtualDisplayControllers.
202 //
203 // TODO(mattcarroll): once PlatformViewsController is refactored into testable
204 // pieces, remove this test and avoid verifying private behavior.
205 VirtualDisplayController fakeVdController1 = mock(VirtualDisplayController.class);
206
207 SingleViewPresentation presentation = fakeVdController1.presentation;
208
209 fakeVdController1.resize(10, 10, null);
210
211 assertEquals(fakeVdController1.presentation != presentation, true);
212 assertEquals(presentation.isShowing(), false);
213 }
214
215 @Test
216 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
218 final int platformViewId = 0;
219 FlutterView fakeFlutterView = new FlutterView(ApplicationProvider.getApplicationContext());
220 VirtualDisplayController fakeVdController = mock(VirtualDisplayController.class);
221 PlatformViewsController platformViewsController = new PlatformViewsController();
222 platformViewsController.vdControllers.put(platformViewId, fakeVdController);
223
224 platformViewsController.attachToView(fakeFlutterView);
225
226 FlutterJNI jni = new FlutterJNI();
227 attach(jni, platformViewsController);
228
229 resize(jni, platformViewsController, platformViewId, 10.0, 20.0);
230
231 ArgumentCaptor<Runnable> resizeCallbackCaptor = ArgumentCaptor.forClass(Runnable.class);
232 verify(fakeVdController, times(1)).resize(anyInt(), anyInt(), resizeCallbackCaptor.capture());
233
234 // Simulate a detach call before the resize completes.
235 platformViewsController.detach();
236
237 // Trigger the callback to ensure that it doesn't crash.
238 resizeCallbackCaptor.getValue().run();
239 }
240
241 @Test
242 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
244 final int platformViewId = 0;
245 FlutterView fakeFlutterView = new FlutterView(ApplicationProvider.getApplicationContext());
246 VirtualDisplayController fakeVdController = mock(VirtualDisplayController.class);
247 PlatformViewsController platformViewsController = new PlatformViewsController();
248 FlutterJNI jni = new FlutterJNI();
249 attach(jni, platformViewsController);
250 platformViewsController.attachToView(fakeFlutterView);
251
252 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
253 PlatformView platformView = mock(PlatformView.class);
254 Context context = ApplicationProvider.getApplicationContext();
255 View androidView = new View(context);
256 when(platformView.getView()).thenReturn(androidView);
257 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
258 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
259 createPlatformView(
260 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
261
262 platformViewsController.vdControllers.put(platformViewId, fakeVdController);
263
264 // Simulate dispose call from the framework.
265 disposePlatformView(jni, platformViewsController, platformViewId);
266
267 // Ensure VirtualDisplayController.dispose() is called
268 verify(fakeVdController, times(1)).dispose();
269 }
270
271 @Test
273 MotionEventTracker motionEventTracker = MotionEventTracker.getInstance();
274 PlatformViewsController platformViewsController = new PlatformViewsController();
275
276 MotionEvent original =
277 MotionEvent.obtain(
278 100, // downTime
279 100, // eventTime
280 1, // action
281 0, // x
282 0, // y
283 0 // metaState
284 );
285
286 // track an event that will later get passed to us from framework
287 MotionEventTracker.MotionEventId motionEventId = motionEventTracker.track(original);
288
289 PlatformViewTouch frameWorkTouch =
290 new PlatformViewTouch(
291 0, // viewId
292 original.getDownTime(),
293 original.getEventTime(),
294 2, // action
295 1, // pointerCount
296 Arrays.asList(Arrays.asList(0, 0)), // pointer properties
297 Arrays.asList(Arrays.asList(0., 1., 2., 3., 4., 5., 6., 7., 8.)), // pointer coords
298 original.getMetaState(),
299 original.getButtonState(),
300 original.getXPrecision(),
301 original.getYPrecision(),
302 original.getDeviceId(),
303 original.getEdgeFlags(),
304 original.getSource(),
305 original.getFlags(),
306 motionEventId.getId());
307
308 MotionEvent resolvedEvent =
309 platformViewsController.toMotionEvent(
310 1, // density
311 frameWorkTouch,
312 true // usingVirtualDisplays
313 );
314
315 assertEquals(resolvedEvent.getAction(), frameWorkTouch.action);
316 assertNotEquals(resolvedEvent.getAction(), original.getAction());
317 }
318
319 @Test
321 MotionEventTracker motionEventTracker = MotionEventTracker.getInstance();
322 PlatformViewsController platformViewsController = new PlatformViewsController();
323
324 MotionEvent original =
325 MotionEvent.obtain(
326 10, // downTime
327 10, // eventTime
328 261, // action
329 0, // x
330 0, // y
331 0 // metaState
332 );
333
334 MotionEventTracker.MotionEventId motionEventId = motionEventTracker.track(original);
335
336 PlatformViewTouch frameWorkTouch =
337 new PlatformViewTouch(
338 0, // viewId
339 original.getDownTime(),
340 original.getEventTime(),
341 0, // action
342 1, // pointerCount
343 Arrays.asList(Arrays.asList(0, 0)), // pointer properties
344 Arrays.asList(Arrays.asList(0., 1., 2., 3., 4., 5., 6., 7., 8.)), // pointer coords
345 original.getMetaState(),
346 original.getButtonState(),
347 original.getXPrecision(),
348 original.getYPrecision(),
349 original.getDeviceId(),
350 original.getEdgeFlags(),
351 original.getSource(),
352 original.getFlags(),
353 motionEventId.getId());
354 MotionEvent resolvedEvent =
355 platformViewsController.toMotionEvent(
356 1, // density
357 frameWorkTouch,
358 false // usingVirtualDisplays
359 );
360 assertEquals(resolvedEvent.getAction(), original.getAction());
361 assertNotEquals(resolvedEvent.getAction(), frameWorkTouch.action);
362 }
363
364 @Ignore
365 @Test
367 MotionEventTracker motionEventTracker = MotionEventTracker.getInstance();
368 PlatformViewsController platformViewsController = new PlatformViewsController();
369
370 MotionEvent original =
371 MotionEvent.obtain(
372 100, // downTime
373 100, // eventTime
374 1, // action
375 0, // x
376 0, // y
377 0 // metaState
378 );
379
380 // track an event that will later get passed to us from framework
381 MotionEventTracker.MotionEventId motionEventId = motionEventTracker.track(original);
382
383 PlatformViewTouch frameWorkTouch =
384 new PlatformViewTouch(
385 0, // viewId
386 original.getDownTime(),
387 original.getEventTime(),
388 2, // action
389 1, // pointerCount
390 Arrays.asList(Arrays.asList(0, 0)), // pointer properties
391 Arrays.asList(Arrays.asList(0., 1., 2., 3., 4., 5., 6., 7., 8.)), // pointer coords
392 original.getMetaState(),
393 original.getButtonState(),
394 original.getXPrecision(),
395 original.getYPrecision(),
396 original.getDeviceId(),
397 original.getEdgeFlags(),
398 original.getSource(),
399 original.getFlags(),
400 motionEventId.getId());
401
402 MotionEvent resolvedEvent =
403 platformViewsController.toMotionEvent(
404 /*density=*/ 1, frameWorkTouch, /*usingVirtualDisplay=*/ false);
405
406 assertEquals(resolvedEvent.getAction(), frameWorkTouch.action);
407 }
408
409 @Test
410 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
412 PlatformViewsController platformViewsController = new PlatformViewsController();
413
414 int platformViewId = 0;
415 assertNull(platformViewsController.getPlatformViewById(platformViewId));
416
417 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
418 PlatformView platformView = mock(PlatformView.class);
419 View androidView = mock(View.class);
420 when(platformView.getView()).thenReturn(androidView);
421 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
422 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
423
424 FlutterJNI jni = new FlutterJNI();
425 attach(jni, platformViewsController);
426
427 // Simulate create call from the framework.
428 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
429
430 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
431
432 View resultAndroidView = platformViewsController.getPlatformViewById(platformViewId);
433 assertNotNull(resultAndroidView);
434 assertEquals(resultAndroidView, androidView);
435 }
436
437 @Test
438 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
440 PlatformViewsController platformViewsController = new PlatformViewsController();
441
442 int platformViewId = 0;
443 assertNull(platformViewsController.getPlatformViewById(platformViewId));
444
445 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
446 PlatformView platformView = mock(PlatformView.class);
447 when(platformView.getView()).thenReturn(mock(View.class));
448 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
449 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
450
451 FlutterJNI jni = new FlutterJNI();
452 attach(jni, platformViewsController);
453
454 // Simulate create call from the framework.
455 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
456 verify(viewFactory, times(1)).create(any(), eq(platformViewId), any());
457 }
458
459 @Test
460 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
462 PlatformViewsController platformViewsController = new PlatformViewsController();
463
464 int platformViewId = 0;
465 assertNull(platformViewsController.getPlatformViewById(platformViewId));
466
467 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
468 PlatformView platformView = mock(PlatformView.class);
469 when(platformView.getView()).thenReturn(mock(View.class));
470 ArgumentCaptor<Context> passedContext = ArgumentCaptor.forClass(Context.class);
471 when(viewFactory.create(passedContext.capture(), eq(platformViewId), any()))
472 .thenReturn(platformView);
473 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
474
475 FlutterJNI jni = new FlutterJNI();
476 attach(jni, platformViewsController);
477
478 // Simulate create call from the framework.
479 createPlatformView(
480 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
481 assertTrue(passedContext.getValue() instanceof MutableContextWrapper);
482 }
483
484 @Test
485 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
487 PlatformViewsController platformViewsController = new PlatformViewsController();
488
489 int platformViewId = 0;
490 assertNull(platformViewsController.getPlatformViewById(platformViewId));
491
492 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
493 PlatformView platformView = mock(PlatformView.class);
494 when(platformView.getView()).thenReturn(mock(View.class));
495 ArgumentCaptor<Context> passedContext = ArgumentCaptor.forClass(Context.class);
496 when(viewFactory.create(passedContext.capture(), eq(platformViewId), any()))
497 .thenReturn(platformView);
498 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
499
500 FlutterJNI jni = new FlutterJNI();
501 attach(jni, platformViewsController);
502
503 // Simulate create call from the framework.
504 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
505 assertFalse(passedContext.getValue() instanceof MutableContextWrapper);
506 }
507
508 @Test
509 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
511 PlatformViewsController platformViewsController = new PlatformViewsController();
512 platformViewsController.setSoftwareRendering(true);
513
514 int platformViewId = 0;
515 assertNull(platformViewsController.getPlatformViewById(platformViewId));
516
517 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
518 PlatformView platformView = mock(PlatformView.class);
519
520 View androidView = mock(View.class);
521 when(platformView.getView()).thenReturn(androidView);
522 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
523 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
524
525 FlutterJNI jni = new FlutterJNI();
526 attach(jni, platformViewsController);
527
528 // Simulate create call from the framework.
529 createPlatformView(
530 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
531 verify(androidView, times(1)).setLayoutDirection(0);
532 }
533
534 @Test
535 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
537 PlatformViewsController platformViewsController = new PlatformViewsController();
538 platformViewsController.setSoftwareRendering(true);
539
540 int platformViewId = 0;
541 assertNull(platformViewsController.getPlatformViewById(platformViewId));
542
543 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
544 PlatformView platformView = mock(PlatformView.class);
545
546 View androidView = mock(View.class);
547 when(platformView.getView()).thenReturn(androidView);
548 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
549 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
550
551 FlutterJNI jni = new FlutterJNI();
552 attach(jni, platformViewsController);
553
554 // Simulate create call from the framework.
555 createPlatformView(
556 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
557
558 ArgumentCaptor<FrameLayout.LayoutParams> layoutParamsCaptor =
559 ArgumentCaptor.forClass(FrameLayout.LayoutParams.class);
560 verify(androidView, times(2)).setLayoutParams(layoutParamsCaptor.capture());
561
562 List<FrameLayout.LayoutParams> capturedLayoutParams = layoutParamsCaptor.getAllValues();
563 assertEquals(capturedLayoutParams.get(0).width, 1);
564 assertEquals(capturedLayoutParams.get(0).height, 1);
565 }
566
567 @Test
568 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
570 PlatformViewsController platformViewsController = new PlatformViewsController();
571 platformViewsController.setSoftwareRendering(true);
572
573 int platformViewId = 0;
574 assertNull(platformViewsController.getPlatformViewById(platformViewId));
575
576 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
577 PlatformView platformView = mock(PlatformView.class);
578
579 View androidView = mock(View.class);
580 when(platformView.getView()).thenReturn(androidView);
581 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
582 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
583
584 FlutterJNI jni = new FlutterJNI();
585 attach(jni, platformViewsController);
586
587 // Simulate create call from the framework.
588 createPlatformView(
589 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
590 verify(androidView, times(1))
591 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
592 }
593
594 @Test
595 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
597 PlatformViewsController platformViewsController = new PlatformViewsController();
598
599 int platformViewId = 0;
600 assertNull(platformViewsController.getPlatformViewById(platformViewId));
601
602 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
603 PlatformView platformView = mock(PlatformView.class);
604 when(platformView.getView()).thenReturn(null);
605 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
606 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
607
608 FlutterJNI jni = new FlutterJNI();
609 attach(jni, platformViewsController);
610
611 // Simulate create call from the framework.
612 createPlatformView(
613 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
614 assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
615
616 assertThrows(
617 IllegalStateException.class,
618 () -> {
619 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
620 });
621 }
622
623 @Test
624 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
626 PlatformViewsController platformViewsController = new PlatformViewsController();
627
628 int platformViewId = 0;
629 assertNull(platformViewsController.getPlatformViewById(platformViewId));
630
631 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
632 PlatformView platformView = mock(PlatformView.class);
633 when(platformView.getView()).thenReturn(null);
634 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
635 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
636
637 FlutterJNI jni = new FlutterJNI();
638 attach(jni, platformViewsController);
639
640 // Simulate create call from the framework.
641 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
642 assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
643
644 assertThrows(
645 IllegalStateException.class,
646 () -> {
647 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
648 });
649 }
650
651 @Test
652 @Config(
653 shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class})
655 PlatformViewsController platformViewsController = new PlatformViewsController();
656
657 int platformViewId = 0;
658 assertNull(platformViewsController.getPlatformViewById(platformViewId));
659
660 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
661 PlatformView platformView = mock(PlatformView.class);
662
663 SurfaceView pv = mock(SurfaceView.class);
664 when(pv.getContext()).thenReturn(mock(MutableContextWrapper.class));
665 when(pv.getLayoutParams()).thenReturn(new LayoutParams(1, 1));
666
667 when(platformView.getView()).thenReturn(pv);
668 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
669 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
670
671 FlutterJNI jni = new FlutterJNI();
672 attach(jni, platformViewsController);
673
674 // Simulate create call from the framework.
675 createPlatformView(
676 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
677
678 assertFalse(platformViewsController.contextToEmbeddedView.isEmpty());
679 platformViewsController.onDetachedFromJNI();
680 assertTrue(platformViewsController.contextToEmbeddedView.isEmpty());
681 }
682
683 @Test
684 @Config(
685 shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class})
687 PlatformViewsController platformViewsController = new PlatformViewsController();
688
689 int platformViewId = 0;
690 assertNull(platformViewsController.getPlatformViewById(platformViewId));
691
692 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
693 PlatformView platformView = mock(PlatformView.class);
694
695 SurfaceView pv = mock(SurfaceView.class);
696 when(pv.getContext()).thenReturn(mock(MutableContextWrapper.class));
697 when(pv.getLayoutParams()).thenReturn(new LayoutParams(1, 1));
698
699 when(platformView.getView()).thenReturn(pv);
700 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
701 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
702
703 FlutterJNI jni = new FlutterJNI();
704 attach(jni, platformViewsController);
705
706 // Simulate create call from the framework.
707 createPlatformView(
708 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
709
710 assertFalse(platformViewsController.contextToEmbeddedView.isEmpty());
711 platformViewsController.onDetachedFromJNI();
712 assertTrue(platformViewsController.contextToEmbeddedView.isEmpty());
713 }
714
715 @Test
716 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
718 PlatformViewsController platformViewsController = new PlatformViewsController();
719
720 int platformViewId = 0;
721 assertNull(platformViewsController.getPlatformViewById(platformViewId));
722
723 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
724 PlatformView platformView = mock(PlatformView.class);
725 View androidView = mock(View.class);
726 when(androidView.getParent()).thenReturn(mock(ViewParent.class));
727 when(platformView.getView()).thenReturn(androidView);
728 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
729 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
730
731 FlutterJNI jni = new FlutterJNI();
732 attach(jni, platformViewsController);
733
734 // Simulate create call from the framework.
735 createPlatformView(
736 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
737 assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
738
739 assertThrows(
740 IllegalStateException.class,
741 () -> {
742 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
743 });
744 }
745
746 @Test
747 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
749 PlatformViewsController platformViewsController = new PlatformViewsController();
750
751 int platformViewId = 0;
752 assertNull(platformViewsController.getPlatformViewById(platformViewId));
753
754 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
755 PlatformView platformView = mock(PlatformView.class);
756 View androidView = mock(View.class);
757 when(androidView.getParent()).thenReturn(mock(ViewParent.class));
758 when(platformView.getView()).thenReturn(androidView);
759 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
760 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
761
762 FlutterJNI jni = new FlutterJNI();
763 attach(jni, platformViewsController);
764
765 // Simulate create call from the framework.
766 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
767 assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
768
769 assertThrows(
770 IllegalStateException.class,
771 () -> {
772 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
773 });
774 }
775
776 @Test
777 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
779 PlatformViewsController platformViewsController = new PlatformViewsController();
780
781 int platformViewId = 0;
782 assertNull(platformViewsController.getPlatformViewById(platformViewId));
783
784 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
785 PlatformView platformView = mock(PlatformView.class);
786 final View androidView = mock(View.class);
787 when(platformView.getView()).thenReturn(androidView);
788 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
789 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
790
791 FlutterJNI jni = new FlutterJNI();
792 attach(jni, platformViewsController);
793
794 verify(androidView, never()).setLayoutDirection(anyInt());
795
796 // Simulate create call from the framework.
797 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
798 assertEquals(ShadowFlutterJNI.getResponses().size(), 1);
799
800 // Simulate set direction call from the framework.
801 setLayoutDirection(jni, platformViewsController, platformViewId, 1);
802 verify(androidView, times(1)).setLayoutDirection(1);
803
804 // The limit value of reply message will be equal to 2 if the layout direction is set
805 // successfully, otherwise it will be much more than 2 due to the reply message contains
806 // an error message wrapped with exception detail information.
807 assertEquals(ShadowFlutterJNI.getResponses().get(0).limit(), 2);
808 }
809
810 @Test
811 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
812 public void resizeAndroidView() {
813 PlatformViewsController platformViewsController = new PlatformViewsController();
814 platformViewsController.setSoftwareRendering(true);
815
816 int platformViewId = 0;
817 assertNull(platformViewsController.getPlatformViewById(platformViewId));
818
819 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
820 PlatformView platformView = mock(PlatformView.class);
821 final View androidView = mock(View.class);
822 when(platformView.getView()).thenReturn(androidView);
823 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
824 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
825
826 FlutterJNI jni = new FlutterJNI();
827 attach(jni, platformViewsController);
828
829 // Simulate create call from the framework.
830 createPlatformView(
831 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
832
833 reset(androidView);
834 when(androidView.getLayoutParams()).thenReturn(new FrameLayout.LayoutParams(0, 0));
835
836 // Simulate a resize call from the framework.
837 resize(jni, platformViewsController, platformViewId, 10.0, 20.0);
838
839 ArgumentCaptor<FrameLayout.LayoutParams> layoutParamsCaptor =
840 ArgumentCaptor.forClass(FrameLayout.LayoutParams.class);
841 verify(androidView, times(1)).setLayoutParams(layoutParamsCaptor.capture());
842
843 assertEquals(layoutParamsCaptor.getValue().width, 10);
844 assertEquals(layoutParamsCaptor.getValue().height, 20);
845 }
846
847 @Test
848 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
850 PlatformViewsController platformViewsController = new PlatformViewsController();
851
852 int platformViewId = 0;
853 assertNull(platformViewsController.getPlatformViewById(platformViewId));
854
855 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
856 PlatformView platformView = mock(PlatformView.class);
857
858 Context context = ApplicationProvider.getApplicationContext();
859 View androidView = new View(context);
860
861 when(platformView.getView()).thenReturn(androidView);
862 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
863 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
864
865 FlutterJNI jni = new FlutterJNI();
866 attach(jni, platformViewsController);
867
868 // Simulate create call from the framework.
869 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
870 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
871
872 assertNotNull(androidView.getParent());
873 assertTrue(androidView.getParent() instanceof FlutterMutatorView);
874
875 // Simulate dispose call from the framework.
876 disposePlatformView(jni, platformViewsController, platformViewId);
877 assertNull(androidView.getParent());
878
879 // Simulate create call from the framework.
880 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
881 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
882
883 assertNotNull(androidView.getParent());
884 assertTrue(androidView.getParent() instanceof FlutterMutatorView);
885 verify(platformView, times(1)).dispose();
886 }
887
888 @Test
889 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
891 PlatformViewsController platformViewsController = new PlatformViewsController();
892
893 int platformViewId = 0;
894 assertNull(platformViewsController.getPlatformViewById(platformViewId));
895
896 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
897 PlatformView platformView = mock(PlatformView.class);
898
899 View androidView = mock(View.class);
900 when(platformView.getView()).thenReturn(androidView);
901 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
902 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
903
904 FlutterJNI jni = new FlutterJNI();
905 attach(jni, platformViewsController);
906
907 // Simulate create call from the framework.
908 createPlatformView(
909 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
910 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
911
912 when(platformView.getView()).thenReturn(null);
913
914 // Simulate dispose call from the framework.
915 disposePlatformView(jni, platformViewsController, platformViewId);
916 verify(platformView, times(1)).dispose();
917 }
918
919 @Test
920 @Config(
921 shadows = {
922 ShadowFlutterSurfaceView.class,
923 ShadowFlutterJNI.class,
924 ShadowPlatformTaskQueue.class
925 })
927 final PlatformViewsController platformViewsController = new PlatformViewsController();
928
929 final int platformViewId = 0;
930 assertNull(platformViewsController.getPlatformViewById(platformViewId));
931
932 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
933 final PlatformView platformView = mock(PlatformView.class);
934 when(platformView.getView()).thenReturn(mock(View.class));
935 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
936
937 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
938
939 final FlutterJNI jni = new FlutterJNI();
940 jni.attachToNative();
941 attach(jni, platformViewsController);
942
943 jni.onFirstFrame();
944
945 // Simulate create call from the framework.
946 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
947
948 // Produce a frame that displays a platform view and an overlay surface.
949 platformViewsController.onBeginFrame();
950 platformViewsController.onDisplayPlatformView(
951 platformViewId,
952 /* x=*/ 0,
953 /* y=*/ 0,
954 /* width=*/ 10,
955 /* height=*/ 10,
956 /* viewWidth=*/ 10,
957 /* viewHeight=*/ 10,
958 /* mutatorsStack=*/ new FlutterMutatorsStack());
959
960 final PlatformOverlayView overlayImageView = mock(PlatformOverlayView.class);
961 when(overlayImageView.acquireLatestImage()).thenReturn(true);
962
963 final FlutterOverlaySurface overlaySurface =
964 platformViewsController.createOverlaySurface(overlayImageView);
965 platformViewsController.onDisplayOverlaySurface(
966 overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
967
968 platformViewsController.onEndFrame();
969
970 // Simulate first frame from the framework.
971 jni.onFirstFrame();
972
973 verify(overlayImageView, never()).detachFromRenderer();
974
975 // Produce a frame that doesn't display platform views.
976 platformViewsController.onBeginFrame();
977 platformViewsController.onEndFrame();
978
979 shadowOf(getMainLooper()).idle();
980 verify(overlayImageView, times(1)).detachFromRenderer();
981 }
982
983 @Test
984 @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
986 final PlatformViewsController platformViewsController = new PlatformViewsController();
987
988 final int platformViewId = 0;
989 assertNull(platformViewsController.getPlatformViewById(platformViewId));
990
991 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
992 final PlatformView platformView = mock(PlatformView.class);
993 final View androidView = mock(View.class);
994 when(platformView.getView()).thenReturn(androidView);
995 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
996
997 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
998
999 final FlutterJNI jni = new FlutterJNI();
1000 jni.attachToNative();
1001 attach(jni, platformViewsController);
1002
1003 jni.onFirstFrame();
1004
1005 // Simulate create call from the framework.
1006 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1007
1008 // Simulate first frame from the framework.
1009 jni.onFirstFrame();
1010 platformViewsController.onBeginFrame();
1011
1012 platformViewsController.onEndFrame();
1013 verify(androidView, never()).setVisibility(View.GONE);
1014
1015 final ViewParent parentView = mock(ViewParent.class);
1016 when(androidView.getParent()).thenReturn(parentView);
1017 }
1018
1019 @Test
1020 @Config(
1021 shadows = {
1022 ShadowFlutterSurfaceView.class,
1023 ShadowFlutterJNI.class,
1024 ShadowPlatformTaskQueue.class
1025 })
1027 final PlatformViewsController platformViewsController = new PlatformViewsController();
1028
1029 final int platformViewId = 0;
1030 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1031
1032 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1033 final PlatformView platformView = mock(PlatformView.class);
1034 final View androidView = mock(View.class);
1035 when(platformView.getView()).thenReturn(androidView);
1036 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1037
1038 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1039
1040 final FlutterJNI jni = new FlutterJNI();
1041 jni.attachToNative();
1042
1043 final FlutterView flutterView = attach(jni, platformViewsController);
1044
1045 jni.onFirstFrame();
1046
1047 // Simulate create call from the framework.
1048 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1049 platformViewsController.initializePlatformViewIfNeeded(platformViewId);
1050 assertEquals(flutterView.getChildCount(), 2);
1051
1052 // Simulate first frame from the framework.
1053 jni.onFirstFrame();
1054 platformViewsController.onBeginFrame();
1055 platformViewsController.onEndFrame();
1056
1057 // Simulate dispose call from the framework.
1058 disposePlatformView(jni, platformViewsController, platformViewId);
1059 assertEquals(flutterView.getChildCount(), 1);
1060 }
1061
1062 @Test
1063 @Config(
1064 shadows = {
1065 ShadowFlutterSurfaceView.class,
1066 ShadowFlutterJNI.class,
1067 ShadowPlatformTaskQueue.class
1068 })
1070 final PlatformViewsController platformViewsController = new PlatformViewsController();
1071
1072 final int platformViewId = 0;
1073 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1074
1075 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1076 final PlatformView platformView = mock(PlatformView.class);
1077 when(platformView.getView()).thenReturn(mock(View.class));
1078 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1079
1080 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1081
1082 final FlutterJNI jni = new FlutterJNI();
1083 jni.attachToNative();
1084 attach(jni, platformViewsController);
1085
1086 jni.onFirstFrame();
1087
1088 // Simulate create call from the framework.
1089 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1090
1091 // Produce a frame that displays a platform view and an overlay surface.
1092 platformViewsController.onBeginFrame();
1093 platformViewsController.onDisplayPlatformView(
1094 platformViewId,
1095 /* x=*/ 0,
1096 /* y=*/ 0,
1097 /* width=*/ 10,
1098 /* height=*/ 10,
1099 /* viewWidth=*/ 10,
1100 /* viewHeight=*/ 10,
1101 /* mutatorsStack=*/ new FlutterMutatorsStack());
1102
1103 final PlatformOverlayView overlayImageView = mock(PlatformOverlayView.class);
1104 when(overlayImageView.acquireLatestImage()).thenReturn(true);
1105
1106 final FlutterOverlaySurface overlaySurface =
1107 platformViewsController.createOverlaySurface(overlayImageView);
1108 // This is OK.
1109 platformViewsController.onDisplayOverlaySurface(
1110 overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
1111
1112 platformViewsController.detach();
1113
1114 verify(overlayImageView, times(1)).closeImageReader();
1115 verify(overlayImageView, times(1)).detachFromRenderer();
1116 }
1117
1118 @Test
1119 @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
1121 final PlatformViewsController platformViewsController = new PlatformViewsController();
1122
1123 final int platformViewId = 0;
1124 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1125
1126 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1127 final PlatformView platformView = mock(PlatformView.class);
1128 when(platformView.getView()).thenReturn(mock(View.class));
1129 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1130
1131 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1132
1133 final FlutterJNI jni = new FlutterJNI();
1134 jni.attachToNative();
1135 attach(jni, platformViewsController);
1136
1137 final FlutterView flutterView = mock(FlutterView.class);
1138 platformViewsController.attachToView(flutterView);
1139
1140 final PlatformOverlayView overlayImageView = mock(PlatformOverlayView.class);
1141 when(overlayImageView.acquireLatestImage()).thenReturn(true);
1142
1143 final FlutterOverlaySurface overlaySurface =
1144 platformViewsController.createOverlaySurface(overlayImageView);
1145
1146 platformViewsController.onDisplayOverlaySurface(
1147 overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
1148
1149 platformViewsController.detachFromView();
1150
1151 verify(overlayImageView, times(1)).closeImageReader();
1152 verify(overlayImageView, times(1)).detachFromRenderer();
1153 verify(flutterView, times(1)).removeView(overlayImageView);
1154 }
1155
1156 @Test
1157 @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
1159 final PlatformViewsController platformViewsController = new PlatformViewsController();
1160
1161 final int platformViewId = 0;
1162 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1163
1164 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1165 final PlatformView platformView = mock(PlatformView.class);
1166 when(platformView.getView()).thenReturn(mock(View.class));
1167 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1168
1169 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1170
1171 final FlutterJNI jni = new FlutterJNI();
1172 jni.attachToNative();
1173 attach(jni, platformViewsController);
1174
1175 final FlutterView flutterView = mock(FlutterView.class);
1176 platformViewsController.attachToView(flutterView);
1177
1178 final PlatformOverlayView overlayImageView = mock(PlatformOverlayView.class);
1179 when(overlayImageView.acquireLatestImage()).thenReturn(true);
1180
1181 final FlutterOverlaySurface overlaySurface =
1182 platformViewsController.createOverlaySurface(overlayImageView);
1183
1184 platformViewsController.onDisplayOverlaySurface(
1185 overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
1186
1187 platformViewsController.detachFromView();
1188
1189 platformViewsController.destroyOverlaySurfaces();
1190 verify(overlayImageView, times(1)).closeImageReader();
1191 verify(overlayImageView, times(1)).detachFromRenderer();
1192 }
1193
1194 @Test
1195 @Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
1197 final PlatformViewsController platformViewsController = new PlatformViewsController();
1198
1199 final int platformViewId = 0;
1200 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1201
1202 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1203 final PlatformView platformView = mock(PlatformView.class);
1204 when(platformView.getView()).thenReturn(mock(View.class));
1205 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1206
1207 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1208
1209 final FlutterJNI jni = new FlutterJNI();
1210 jni.attachToNative();
1211 attach(jni, platformViewsController);
1212
1213 final FlutterView flutterView = mock(FlutterView.class);
1214 platformViewsController.attachToView(flutterView);
1215
1216 final PlatformOverlayView overlayImageView = mock(PlatformOverlayView.class);
1217 when(overlayImageView.acquireLatestImage()).thenReturn(true);
1218
1219 final FlutterOverlaySurface overlaySurface =
1220 platformViewsController.createOverlaySurface(overlayImageView);
1221
1222 platformViewsController.onDisplayOverlaySurface(
1223 overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
1224
1225 platformViewsController.destroyOverlaySurfaces();
1226 verify(flutterView, never()).removeView(overlayImageView);
1227 }
1228
1229 @Test
1231 final PlatformViewsController platformViewsController = new PlatformViewsController();
1232 boolean shouldProxying = platformViewsController.checkInputConnectionProxy(null);
1233 assertFalse(shouldProxying);
1234 }
1235
1236 @Test
1237 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
1239 final PlatformViewsController platformViewsController = new PlatformViewsController();
1240
1241 final int platformViewId = 0;
1242 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1243
1244 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1245 final PlatformView platformView = mock(PlatformView.class);
1246 final View androidView = mock(View.class);
1247 when(platformView.getView()).thenReturn(androidView);
1248 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1249
1250 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1251
1252 final FlutterJNI jni = new FlutterJNI();
1253 jni.attachToNative();
1254 final FlutterView flutterView = attach(jni, platformViewsController);
1255
1256 jni.onFirstFrame();
1257
1258 // Simulate create call from the framework.
1259 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1260
1261 // Produce a frame that displays a platform view and an overlay surface.
1262 platformViewsController.onBeginFrame();
1263 platformViewsController.onDisplayPlatformView(
1264 platformViewId,
1265 /* x=*/ 0,
1266 /* y=*/ 0,
1267 /* width=*/ 10,
1268 /* height=*/ 10,
1269 /* viewWidth=*/ 10,
1270 /* viewHeight=*/ 10,
1271 /* mutatorsStack=*/ new FlutterMutatorsStack());
1272
1273 assertEquals(flutterView.getChildCount(), 3);
1274
1275 final View view = flutterView.getChildAt(1);
1276 assertTrue(view instanceof FlutterImageView);
1277
1278 // Simulate dispose call from the framework.
1279 disposePlatformView(jni, platformViewsController, platformViewId);
1280 }
1281
1282 @Test
1283 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
1285 final PlatformViewsController platformViewsController = new PlatformViewsController();
1286
1287 final int platformViewId = 0;
1288 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1289
1290 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1291 final PlatformView platformView = mock(PlatformView.class);
1292 final View androidView = mock(View.class);
1293 when(platformView.getView()).thenReturn(androidView);
1294 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1295
1296 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1297
1298 final FlutterJNI jni = new FlutterJNI();
1299 jni.attachToNative();
1300 final FlutterView flutterView = attach(jni, platformViewsController);
1301
1302 jni.onFirstFrame();
1303
1304 // Simulate setting render surface conversion flag.
1305 synchronizeToNativeViewHierarchy(jni, platformViewsController, false);
1306
1307 // Simulate create call from the framework.
1308 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1309
1310 // Produce a frame that displays a platform view and an overlay surface.
1311 platformViewsController.onBeginFrame();
1312 platformViewsController.onDisplayPlatformView(
1313 platformViewId,
1314 /* x=*/ 0,
1315 /* y=*/ 0,
1316 /* width=*/ 10,
1317 /* height=*/ 10,
1318 /* viewWidth=*/ 10,
1319 /* viewHeight=*/ 10,
1320 /* mutatorsStack=*/ new FlutterMutatorsStack());
1321
1322 assertEquals(flutterView.getChildCount(), 2);
1323 assertTrue(!(flutterView.getChildAt(0) instanceof PlatformOverlayView));
1324 assertTrue(flutterView.getChildAt(1) instanceof FlutterMutatorView);
1325
1326 // Simulate dispose call from the framework.
1327 disposePlatformView(jni, platformViewsController, platformViewId);
1328 }
1329
1330 @Test
1331 @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
1333 PlatformViewsController platformViewsController = new PlatformViewsController();
1334 platformViewsController.setSoftwareRendering(true);
1335
1336 int platformViewId = 100;
1337 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1338
1339 PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1340 PlatformView platformView = mock(PlatformView.class);
1341 View androidView = mock(View.class);
1342 when(platformView.getView()).thenReturn(androidView);
1343 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1344 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1345
1346 FlutterJNI jni = new FlutterJNI();
1347 FlutterView initFlutterView = mock(FlutterView.class);
1348 attachToFlutterView(jni, platformViewsController, initFlutterView);
1349
1350 createPlatformView(
1351 jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false);
1352 verify(initFlutterView, times(1)).addView(any(PlatformViewWrapper.class));
1353
1354 platformViewsController.detachFromView();
1355 verify(initFlutterView, times(1)).removeView(any(PlatformViewWrapper.class));
1356
1357 FlutterView newFlutterView = mock(FlutterView.class);
1358 platformViewsController.attachToView(newFlutterView);
1359 verify(newFlutterView, times(1)).addView(any(PlatformViewWrapper.class));
1360 }
1361
1362 @Config(
1363 shadows = {
1364 ShadowFlutterSurfaceView.class,
1365 ShadowFlutterJNI.class,
1366 ShadowPlatformTaskQueue.class
1367 })
1369 final PlatformViewsController platformViewsController = new PlatformViewsController();
1370
1371 final int platformViewId = 0;
1372 assertNull(platformViewsController.getPlatformViewById(platformViewId));
1373
1374 final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
1375 final PlatformView platformView = mock(PlatformView.class);
1376 final View androidView = mock(View.class);
1377 when(platformView.getView()).thenReturn(androidView);
1378 when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
1379
1380 platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
1381
1382 final FlutterJNI jni = new FlutterJNI();
1383 jni.attachToNative();
1384
1385 final FlutterView flutterView = attach(jni, platformViewsController);
1386
1387 jni.onFirstFrame();
1388
1389 // Simulate create call from the framework.
1390 createPlatformView(jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ true);
1391
1392 // The simulation creates an Overlay on top of the PlatformView
1393 // This is going to be called `flutterView.convertToImageView`
1394 platformViewsController.createOverlaySurface();
1395 platformViewsController.onDisplayOverlaySurface(platformViewId, 0, 0, 10, 10);
1396
1397 // This will contain three views: Background ImageView、PlatformView、Overlay ImageView
1398 assertEquals(flutterView.getChildCount(), 3);
1399
1400 FlutterImageView imageView = flutterView.getCurrentImageSurface();
1401
1402 // Make sure the ImageView is inside the current FlutterView.
1403 assertTrue(imageView != null);
1404 assertTrue(flutterView.indexOfChild(imageView) != -1);
1405
1406 // Make sure the overlayView is inside the current FlutterView
1407 assertTrue(platformViewsController.getOverlayLayerViews().size() != 0);
1408 PlatformOverlayView overlayView = platformViewsController.getOverlayLayerViews().get(0);
1409 assertTrue(overlayView != null);
1410 assertTrue(flutterView.indexOfChild(overlayView) != -1);
1411
1412 // Simulate in a new frame, there's no PlatformView, which is called
1413 // `flutterView.revertImageView`. And register a `FlutterUiDisplayListener` callback.
1414 // During callback execution it will invoke `flutterImageView.detachFromRenderer()`.
1415 platformViewsController.onBeginFrame();
1416 platformViewsController.onEndFrame();
1417
1418 // Invoke all registered `FlutterUiDisplayListener` callback
1419 jni.onFirstFrame();
1420
1421 assertEquals(null, flutterView.getCurrentImageSurface());
1422
1423 // Make sure the background ImageVIew is not in the FlutterView
1424 assertTrue(flutterView.indexOfChild(imageView) == -1);
1425
1426 // Make sure the overlay ImageVIew is not in the FlutterView
1427 assertTrue(flutterView.indexOfChild(overlayView) == -1);
1428 }
1429
1430 private static ByteBuffer encodeMethodCall(MethodCall call) {
1431 final ByteBuffer buffer = StandardMethodCodec.INSTANCE.encodeMethodCall(call);
1432 buffer.rewind();
1433 return buffer;
1434 }
1435
1436 private static void createPlatformView(
1437 FlutterJNI jni,
1438 PlatformViewsController platformViewsController,
1439 int platformViewId,
1440 String viewType,
1441 boolean hybrid) {
1442 final Map<String, Object> args = new HashMap<>();
1443 args.put("hybrid", hybrid);
1444 args.put("id", platformViewId);
1445 args.put("viewType", viewType);
1446 args.put("direction", 0);
1447 args.put("width", 1.0);
1448 args.put("height", 1.0);
1449
1450 final MethodCall platformCreateMethodCall = new MethodCall("create", args);
1451
1452 jni.handlePlatformMessage(
1453 "flutter/platform_views",
1454 encodeMethodCall(platformCreateMethodCall),
1455 /*replyId=*/ 0,
1456 /*messageData=*/ 0);
1457 }
1458
1459 private static void setLayoutDirection(
1460 FlutterJNI jni,
1461 PlatformViewsController platformViewsController,
1462 int platformViewId,
1463 int direction) {
1464 final Map<String, Object> args = new HashMap<>();
1465 args.put("id", platformViewId);
1466 args.put("direction", direction);
1467
1468 final MethodCall platformSetDirectionMethodCall = new MethodCall("setDirection", args);
1469
1470 jni.handlePlatformMessage(
1471 "flutter/platform_views",
1472 encodeMethodCall(platformSetDirectionMethodCall),
1473 /*replyId=*/ 0,
1474 /*messageData=*/ 0);
1475 }
1476
1477 private static void resize(
1478 FlutterJNI jni,
1479 PlatformViewsController platformViewsController,
1480 int platformViewId,
1481 double width,
1482 double height) {
1483 final Map<String, Object> args = new HashMap<>();
1484 args.put("id", platformViewId);
1485 args.put("width", width);
1486 args.put("height", height);
1487
1488 final MethodCall platformResizeMethodCall = new MethodCall("resize", args);
1489
1490 jni.handlePlatformMessage(
1491 "flutter/platform_views",
1492 encodeMethodCall(platformResizeMethodCall),
1493 /*replyId=*/ 0,
1494 /*messageData=*/ 0);
1495 }
1496
1497 private static void disposePlatformView(
1498 FlutterJNI jni, PlatformViewsController platformViewsController, int platformViewId) {
1499
1500 final Map<String, Object> args = new HashMap<>();
1501 args.put("hybrid", true);
1502 args.put("id", platformViewId);
1503
1504 final MethodCall platformDisposeMethodCall = new MethodCall("dispose", args);
1505
1506 jni.handlePlatformMessage(
1507 "flutter/platform_views",
1508 encodeMethodCall(platformDisposeMethodCall),
1509 /*replyId=*/ 0,
1510 /*messageData=*/ 0);
1511 }
1512
1513 private static void synchronizeToNativeViewHierarchy(
1514 FlutterJNI jni, PlatformViewsController platformViewsController, boolean yes) {
1515
1516 final MethodCall convertMethodCall = new MethodCall("synchronizeToNativeViewHierarchy", yes);
1517
1518 jni.handlePlatformMessage(
1519 "flutter/platform_views",
1520 encodeMethodCall(convertMethodCall),
1521 /*replyId=*/ 0,
1522 /*messageData=*/ 0);
1523 }
1524
1525 private static FlutterView attach(
1526 FlutterJNI jni, PlatformViewsController platformViewsController) {
1527 final Context context = ApplicationProvider.getApplicationContext();
1528 final FlutterView flutterView =
1529 new FlutterView(context, new FlutterSurfaceView(context)) {
1530 @Override
1531 public FlutterImageView createImageView() {
1532 final FlutterImageView view = mock(FlutterImageView.class);
1533 when(view.acquireLatestImage()).thenReturn(true);
1534 return mock(FlutterImageView.class);
1535 }
1536 };
1537 attachToFlutterView(jni, platformViewsController, flutterView);
1538 return flutterView;
1539 }
1540
1541 private static void attachToFlutterView(
1542 FlutterJNI jni, PlatformViewsController platformViewsController, FlutterView flutterView) {
1543 final DartExecutor executor = new DartExecutor(jni, mock(AssetManager.class));
1544 executor.onAttachedToJNI();
1545
1546 final Context context = ApplicationProvider.getApplicationContext();
1547 final TextureRegistry registry =
1548 new TextureRegistry() {
1549 public void TextureRegistry() {}
1550
1551 @Override
1552 public SurfaceTextureEntry createSurfaceTexture() {
1553 return registerSurfaceTexture(mock(SurfaceTexture.class));
1554 }
1555
1556 @Override
1557 public SurfaceTextureEntry registerSurfaceTexture(SurfaceTexture surfaceTexture) {
1558 return new SurfaceTextureEntry() {
1559 @NonNull
1560 @Override
1561 public SurfaceTexture surfaceTexture() {
1562 return mock(SurfaceTexture.class);
1563 }
1564
1565 @Override
1566 public long id() {
1567 return 0;
1568 }
1569
1570 @Override
1571 public void release() {}
1572 };
1573 }
1574
1575 @Override
1576 public ImageTextureEntry createImageTexture() {
1577 return new ImageTextureEntry() {
1578 @Override
1579 public long id() {
1580 return 0;
1581 }
1582
1583 @Override
1584 public void release() {}
1585
1586 @Override
1587 public void pushImage(Image image) {}
1588 };
1589 }
1590
1591 @Override
1592 public SurfaceProducer createSurfaceProducer() {
1593 return new SurfaceProducer() {
1594 @Override
1595 public long id() {
1596 return 0;
1597 }
1598
1599 @Override
1600 public void release() {}
1601
1602 @Override
1603 public int getWidth() {
1604 return 0;
1605 }
1606
1607 @Override
1608 public int getHeight() {
1609 return 0;
1610 }
1611
1612 @Override
1613 public void setSize(int width, int height) {}
1614
1615 @Override
1616 public Surface getSurface() {
1617 return null;
1618 }
1619
1620 public void scheduleFrame() {}
1621 };
1622 }
1623 };
1624
1625 platformViewsController.attach(context, registry, executor);
1626
1627 final FlutterEngine engine = mock(FlutterEngine.class);
1628 when(engine.getRenderer()).thenReturn(new FlutterRenderer(jni));
1629 when(engine.getMouseCursorChannel()).thenReturn(mock(MouseCursorChannel.class));
1630 when(engine.getTextInputChannel()).thenReturn(mock(TextInputChannel.class));
1631 when(engine.getSettingsChannel()).thenReturn(new SettingsChannel(executor));
1632 when(engine.getPlatformViewsController()).thenReturn(platformViewsController);
1633 when(engine.getLocalizationPlugin()).thenReturn(mock(LocalizationPlugin.class));
1634 when(engine.getAccessibilityChannel()).thenReturn(mock(AccessibilityChannel.class));
1635 when(engine.getDartExecutor()).thenReturn(executor);
1636
1637 flutterView.attachToFlutterEngine(engine);
1638 platformViewsController.attachToView(flutterView);
1639 }
1640
1641 /**
1642 * For convenience when writing tests, this allows us to make fake messages from Flutter via
1643 * Platform Channels. Typically those calls happen on the ui thread which dispatches to the
1644 * platform thread. Since tests run on the platform thread it makes it difficult to test without
1645 * this, but isn't technically required.
1646 */
1647 @Implements(io.flutter.embedding.engine.dart.PlatformTaskQueue.class)
1648 public static class ShadowPlatformTaskQueue {
1649 @Implementation
1650 public void dispatch(Runnable runnable) {
1651 runnable.run();
1652 }
1653 }
1654
1655 /**
1656 * The shadow class of {@link Presentation} to simulate Presentation showing logic.
1657 *
1658 * <p>Robolectric doesn't support VirtualDisplay creating correctly now, so this shadow class is
1659 * used to simulate custom logic for Presentation.
1660 */
1661 @Implements(Presentation.class)
1662 public static class ShadowPresentation extends ShadowDialog {
1663 private boolean isShowing = false;
1664
1666
1667 @Implementation
1668 protected void show() {
1669 isShowing = true;
1670 }
1671
1672 @Implementation
1673 protected void dismiss() {
1674 isShowing = false;
1675 }
1676
1677 @Implementation
1678 protected boolean isShowing() {
1679 return isShowing;
1680 }
1681 }
1682
1683 @Implements(FlutterJNI.class)
1684 public static class ShadowFlutterJNI {
1685 private static SparseArray<ByteBuffer> replies = new SparseArray<>();
1686
1688
1689 @Implementation
1691 return false;
1692 }
1693
1694 @Implementation
1695 public long performNativeAttach(FlutterJNI flutterJNI) {
1696 return 1;
1697 }
1698
1699 @Implementation
1701 String channel, ByteBuffer message, int position, int responseId) {}
1702
1703 @Implementation
1705
1706 @Implementation
1707 public void onSurfaceDestroyed() {}
1708
1709 @Implementation
1711
1712 @Implementation
1714 float devicePixelRatio,
1715 int physicalWidth,
1716 int physicalHeight,
1717 int physicalPaddingTop,
1718 int physicalPaddingRight,
1719 int physicalPaddingBottom,
1720 int physicalPaddingLeft,
1721 int physicalViewInsetTop,
1722 int physicalViewInsetRight,
1723 int physicalViewInsetBottom,
1724 int physicalViewInsetLeft,
1725 int systemGestureInsetTop,
1726 int systemGestureInsetRight,
1727 int systemGestureInsetBottom,
1728 int systemGestureInsetLeft,
1729 int physicalTouchSlop,
1730 int[] displayFeaturesBounds,
1731 int[] displayFeaturesType,
1732 int[] displayFeaturesState) {}
1733
1734 @Implementation
1736 int responseId, ByteBuffer message, int position) {
1737 replies.put(responseId, message);
1738 }
1739
1740 public static SparseArray<ByteBuffer> getResponses() {
1741 return replies;
1742 }
1743 }
1744
1745 @Implements(SurfaceView.class)
1746 public static class ShadowFlutterSurfaceView extends ShadowSurfaceView {
1747 private final FakeSurfaceHolder holder = new FakeSurfaceHolder();
1748
1749 public static class FakeSurfaceHolder extends ShadowSurfaceView.FakeSurfaceHolder {
1750 private final Surface surface = mock(Surface.class);
1751
1753 return surface;
1754 }
1755
1756 @Implementation
1757 public void addCallback(SurfaceHolder.Callback callback) {
1758 callback.surfaceCreated(this);
1759 }
1760 }
1761
1763
1764 @Implementation
1765 public SurfaceHolder getHolder() {
1766 return holder;
1767 }
1768 }
1769}
static SkISize times(const SkISize &size, float factor)
m reset()
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition M44Test.cpp:18
abstract PlatformView create(Context context, int viewId, @Nullable Object args)
void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position)
void dispatchPlatformMessage(String channel, ByteBuffer message, int position, int responseId)
void setViewportMetrics(float devicePixelRatio, int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, int physicalViewInsetBottom, int physicalViewInsetLeft, int systemGestureInsetTop, int systemGestureInsetRight, int systemGestureInsetBottom, int systemGestureInsetLeft, int physicalTouchSlop, int[] displayFeaturesBounds, int[] displayFeaturesType, int[] displayFeaturesState)
MotionEvent toMotionEvent(float density, PlatformViewsChannel.PlatformViewTouch touch, boolean usingVirtualDiplay)
void onDisplayPlatformView(int viewId, int x, int y, int width, int height, int viewWidth, int viewHeight, @NonNull FlutterMutatorsStack mutatorsStack)
void attachToView(@NonNull FlutterView newFlutterView)
FlutterOverlaySurface createOverlaySurface(@NonNull PlatformOverlayView imageView)
void onDisplayOverlaySurface(int id, int x, int y, int width, int height)
final HashMap< Integer, VirtualDisplayController > vdControllers
void resize(final int width, final int height, final Runnable onNewSizeFrameAvailable)
FlutterEngine engine
Definition main.cc:68
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
static const uint8_t buffer[]
static ::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
boolean registerViewFactory(@NonNull String viewTypeId, @NonNull PlatformViewFactory factory)
Win32Message message
int32_t height
int32_t width
const uintptr_t id