Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
PlatformPluginTest.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.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
8import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
9import static io.flutter.Build.API_LEVELS;
10import static org.junit.Assert.assertEquals;
11import static org.junit.Assert.assertFalse;
12import static org.junit.Assert.assertNotNull;
13import static org.junit.Assert.assertNull;
14import static org.junit.Assert.assertTrue;
15import static org.junit.Assert.fail;
16import static org.mockito.Mockito.any;
17import static org.mockito.Mockito.anyBoolean;
18import static org.mockito.Mockito.doThrow;
19import static org.mockito.Mockito.mock;
20import static org.mockito.Mockito.mockStatic;
21import static org.mockito.Mockito.never;
22import static org.mockito.Mockito.spy;
23import static org.mockito.Mockito.times;
24import static org.mockito.Mockito.verify;
25import static org.mockito.Mockito.when;
26
27import android.app.Activity;
28import android.content.ClipData;
29import android.content.ClipboardManager;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.res.AssetFileDescriptor;
34import android.net.Uri;
35import android.os.Build;
36import android.view.View;
37import android.view.Window;
38import android.view.WindowInsetsController;
39import android.view.WindowManager;
40import androidx.activity.OnBackPressedCallback;
41import androidx.fragment.app.FragmentActivity;
42import androidx.test.core.app.ApplicationProvider;
43import io.flutter.embedding.engine.systemchannels.PlatformChannel;
44import io.flutter.embedding.engine.systemchannels.PlatformChannel.Brightness;
45import io.flutter.embedding.engine.systemchannels.PlatformChannel.ClipboardContentFormat;
46import io.flutter.embedding.engine.systemchannels.PlatformChannel.SystemChromeStyle;
47import io.flutter.plugin.platform.PlatformPlugin.PlatformPluginDelegate;
48import java.io.IOException;
49import java.util.concurrent.atomic.AtomicBoolean;
50import org.junit.Test;
51import org.junit.runner.RunWith;
52import org.mockito.ArgumentCaptor;
53import org.mockito.MockedStatic;
54import org.robolectric.Robolectric;
55import org.robolectric.RobolectricTestRunner;
56import org.robolectric.android.controller.ActivityController;
57import org.robolectric.annotation.Config;
58import org.robolectric.shadows.ShadowLooper;
59
60@Config(manifest = Config.NONE)
61@RunWith(RobolectricTestRunner.class)
62public class PlatformPluginTest {
63 private final Context ctx = ApplicationProvider.getApplicationContext();
64 private final PlatformChannel mockPlatformChannel = mock(PlatformChannel.class);
65
66 private ClipboardManager clipboardManager;
67 private ClipboardContentFormat clipboardFormat;
68
69 public void setUpForTextClipboardTests(Activity mockActivity) {
70 clipboardManager = spy(ctx.getSystemService(ClipboardManager.class));
71 when(mockActivity.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(clipboardManager);
72 clipboardFormat = ClipboardContentFormat.PLAIN_TEXT;
73 }
74
75 @Test
77 View fakeDecorView = mock(View.class);
78 Window fakeWindow = mock(Window.class);
79 Activity mockActivity = mock(Activity.class);
80 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
81 when(mockActivity.getWindow()).thenReturn(fakeWindow);
82 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
83 setUpForTextClipboardTests(mockActivity);
84
85 // Primary clip contains non-text media.
86 ClipData clip = ClipData.newPlainText("label", "Text");
87 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
88 clipboardManager.setPrimaryClip(clip);
89 assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
90 }
91
92 @Test
94 View fakeDecorView = mock(View.class);
95 Window fakeWindow = mock(Window.class);
96 Activity mockActivity = mock(Activity.class);
97 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
98 when(mockActivity.getWindow()).thenReturn(fakeWindow);
99 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
100 setUpForTextClipboardTests(mockActivity);
101 ContentResolver contentResolver = mock(ContentResolver.class);
102 ClipData.Item mockItem = mock(ClipData.Item.class);
103 Uri mockUri = mock(Uri.class);
104 AssetFileDescriptor mockAssetFileDescriptor = mock(AssetFileDescriptor.class);
105
106 when(mockActivity.getContentResolver()).thenReturn(contentResolver);
107 when(mockUri.getScheme()).thenReturn("content");
108 when(mockItem.getText()).thenReturn(null);
109 when(mockItem.getUri()).thenReturn(mockUri);
110 when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any()))
111 .thenReturn(mockAssetFileDescriptor);
112 when(mockItem.coerceToText(mockActivity)).thenReturn("something non-null");
113 doThrow(new IOException()).when(mockAssetFileDescriptor).close();
114
115 ClipData clip = new ClipData("label", new String[0], mockItem);
116
117 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
118 clipboardManager.setPrimaryClip(clip);
119 assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
120 }
121
122 @Test
124 throws IOException {
125 View fakeDecorView = mock(View.class);
126 Window fakeWindow = mock(Window.class);
127 Activity mockActivity = mock(Activity.class);
128 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
129 when(mockActivity.getWindow()).thenReturn(fakeWindow);
130 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
131 setUpForTextClipboardTests(mockActivity);
132 ContentResolver contentResolver = mock(ContentResolver.class);
133 ClipData.Item mockItem = mock(ClipData.Item.class);
134 Uri mockUri = mock(Uri.class);
135
136 when(mockActivity.getContentResolver()).thenReturn(contentResolver);
137 when(mockUri.getScheme()).thenReturn("content");
138 when(mockItem.getText()).thenReturn(null);
139 when(mockItem.getUri()).thenReturn(mockUri);
140 when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any()))
141 .thenReturn(mock(AssetFileDescriptor.class));
142 when(mockItem.coerceToText(mockActivity)).thenReturn("something non-null");
143
144 ClipData clip = new ClipData("label", new String[0], mockItem);
145
146 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
147 clipboardManager.setPrimaryClip(clip);
148 assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
149 }
150
151 @Test
153 throws IOException {
154 View fakeDecorView = mock(View.class);
155 Window fakeWindow = mock(Window.class);
156 Activity mockActivity = mock(Activity.class);
157 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
158 when(mockActivity.getWindow()).thenReturn(fakeWindow);
159 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
160 setUpForTextClipboardTests(mockActivity);
161 ContentResolver contentResolver = ctx.getContentResolver();
162
163 when(mockActivity.getContentResolver()).thenReturn(contentResolver);
164
165 Uri uri = Uri.parse("content://media/external_primary/images/media/");
166 ClipData clip = ClipData.newUri(contentResolver, "URI", uri);
167
168 clipboardManager.setPrimaryClip(clip);
169 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
170 }
171
172 @Test
174 View fakeDecorView = mock(View.class);
175 Window fakeWindow = mock(Window.class);
176 Activity mockActivity = mock(Activity.class);
177 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
178 when(mockActivity.getWindow()).thenReturn(fakeWindow);
179 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
180 setUpForTextClipboardTests(mockActivity);
181 ContentResolver contentResolver = ctx.getContentResolver();
182
183 when(mockActivity.getContentResolver()).thenReturn(contentResolver);
184
185 Uri uri = Uri.parse("file:///");
186 ClipData clip = ClipData.newUri(contentResolver, "URI", uri);
187
188 clipboardManager.setPrimaryClip(clip);
189 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
190 }
191
192 @Test
194 View fakeDecorView = mock(View.class);
195 Window fakeWindow = mock(Window.class);
196 Activity mockActivity = mock(Activity.class);
197 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
198 when(mockActivity.getWindow()).thenReturn(fakeWindow);
199 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
200 setUpForTextClipboardTests(mockActivity);
201 ClipData.Item mockItem = mock(ClipData.Item.class);
202
203 when(mockItem.getText()).thenReturn(null);
204 when(mockItem.getUri()).thenReturn(null);
205
206 ClipData clip = new ClipData("label", new String[0], mockItem);
207
208 clipboardManager.setPrimaryClip(clip);
209 assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
210 }
211
212 @SuppressWarnings("deprecation")
213 // ClipboardManager.getText
214 @Config(sdk = API_LEVELS.API_28)
215 @Test
217 View fakeDecorView = mock(View.class);
218 Window fakeWindow = mock(Window.class);
219 Activity mockActivity = mock(Activity.class);
220 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
221 when(mockActivity.getWindow()).thenReturn(fakeWindow);
222 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
223 setUpForTextClipboardTests(mockActivity);
224
225 // Plain text
226 ClipData clip = ClipData.newPlainText("label", "Text");
227 clipboardManager.setPrimaryClip(clip);
228 assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
229
230 // Empty plain text
231 clip = ClipData.newPlainText("", "");
232 clipboardManager.setPrimaryClip(clip);
233 // Without actually accessing clipboard data (preferred behavior), it is not possible to
234 // distinguish between empty and non-empty string contents.
235 assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
236
237 // HTML text
238 clip = ClipData.newHtmlText("motto", "Don't be evil", "<b>Don't</b> be evil");
239 clipboardManager.setPrimaryClip(clip);
240 assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
241
242 // Text MIME type
243 clip = new ClipData("label", new String[] {"text/something"}, new ClipData.Item("content"));
244 clipboardManager.setPrimaryClip(clip);
245 assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
246
247 // Other MIME type
248 clip =
249 new ClipData(
250 "label", new String[] {"application/octet-stream"}, new ClipData.Item("content"));
251 clipboardManager.setPrimaryClip(clip);
252 assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
253
254 if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
255 // Empty clipboard
256 clipboardManager.clearPrimaryClip();
257 assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
258 }
259
260 // Verify that the clipboard contents are never accessed.
261 verify(clipboardManager, never()).getPrimaryClip();
262 verify(clipboardManager, never()).getText();
263 }
264
265 @Config(sdk = API_LEVELS.API_29)
266 @Test
268 View fakeDecorView = mock(View.class);
269 Window fakeWindow = mock(Window.class);
270 Activity mockActivity = mock(Activity.class);
271 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
272 when(mockActivity.getWindow()).thenReturn(fakeWindow);
273 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
274
275 if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
276 // Default style test
277 SystemChromeStyle style =
278 new SystemChromeStyle(
279 0XFF000000, // statusBarColor
280 Brightness.LIGHT, // statusBarIconBrightness
281 true, // systemStatusBarContrastEnforced
282 0XFFC70039, // systemNavigationBarColor
283 Brightness.LIGHT, // systemNavigationBarIconBrightness
284 0XFF006DB3, // systemNavigationBarDividerColor
285 true); // systemNavigationBarContrastEnforced
286
287 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
288
289 verify(fakeWindow).setStatusBarColor(0xFF000000);
290 verify(fakeWindow).setNavigationBarColor(0XFFC70039);
291 verify(fakeWindow).setNavigationBarDividerColor(0XFF006DB3);
292 verify(fakeWindow).setStatusBarContrastEnforced(true);
293 verify(fakeWindow).setNavigationBarContrastEnforced(true);
294
295 // Regression test for https://github.com/flutter/flutter/issues/88431
296 // A null brightness should not affect changing color settings.
297 style =
298 new SystemChromeStyle(
299 0XFF006DB3, // statusBarColor
300 null, // statusBarIconBrightness
301 false, // systemStatusBarContrastEnforced
302 0XFF000000, // systemNavigationBarColor
303 null, // systemNavigationBarIconBrightness
304 0XFF006DB3, // systemNavigationBarDividerColor
305 false); // systemNavigationBarContrastEnforced
306
307 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
308
309 verify(fakeWindow).setStatusBarColor(0XFF006DB3);
310 verify(fakeWindow).setNavigationBarColor(0XFF000000);
311 verify(fakeWindow, times(2)).setNavigationBarDividerColor(0XFF006DB3);
312 verify(fakeWindow).setStatusBarContrastEnforced(false);
313 verify(fakeWindow).setNavigationBarContrastEnforced(false);
314
315 // Null contrasts values should be allowed.
316 style =
317 new SystemChromeStyle(
318 0XFF006DB3, // statusBarColor
319 null, // statusBarIconBrightness
320 null, // systemStatusBarContrastEnforced
321 0XFF000000, // systemNavigationBarColor
322 null, // systemNavigationBarIconBrightness
323 0XFF006DB3, // systemNavigationBarDividerColor
324 null); // systemNavigationBarContrastEnforced
325
326 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
327
328 verify(fakeWindow, times(2)).setStatusBarColor(0XFF006DB3);
329 verify(fakeWindow, times(2)).setNavigationBarColor(0XFF000000);
330 verify(fakeWindow, times(3)).setNavigationBarDividerColor(0XFF006DB3);
331 // Count is 1 each from earlier calls
332 verify(fakeWindow, times(1)).setStatusBarContrastEnforced(true);
333 verify(fakeWindow, times(1)).setNavigationBarContrastEnforced(true);
334 verify(fakeWindow, times(1)).setStatusBarContrastEnforced(false);
335 verify(fakeWindow, times(1)).setNavigationBarContrastEnforced(false);
336 }
337 }
338
339 @Config(sdk = API_LEVELS.API_30)
340 @Test
342 View fakeDecorView = mock(View.class);
343 Window fakeWindow = mock(Window.class);
344 Activity mockActivity = mock(Activity.class);
345 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
346 when(mockActivity.getWindow()).thenReturn(fakeWindow);
347 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
348
349 if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
350 WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class);
351 when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController);
352
353 SystemChromeStyle style =
354 new SystemChromeStyle(
355 null, // statusBarColor
356 null, // statusBarIconBrightness
357 null, // systemStatusBarContrastEnforced
358 null, // systemNavigationBarColor
359 Brightness.LIGHT, // systemNavigationBarIconBrightness
360 null, // systemNavigationBarDividerColor
361 null); // systemNavigationBarContrastEnforced
362
363 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
364
365 verify(fakeWindowInsetsController)
366 .setSystemBarsAppearance(0, APPEARANCE_LIGHT_NAVIGATION_BARS);
367
368 style =
369 new SystemChromeStyle(
370 null, // statusBarColor
371 null, // statusBarIconBrightness
372 null, // systemStatusBarContrastEnforced
373 null, // systemNavigationBarColor
374 Brightness.DARK, // systemNavigationBarIconBrightness
375 null, // systemNavigationBarDividerColor
376 null); // systemNavigationBarContrastEnforced
377
378 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
379
380 verify(fakeWindowInsetsController)
381 .setSystemBarsAppearance(
382 APPEARANCE_LIGHT_NAVIGATION_BARS, APPEARANCE_LIGHT_NAVIGATION_BARS);
383 }
384 }
385
386 @Config(sdk = API_LEVELS.API_30)
387 @Test
389 View fakeDecorView = mock(View.class);
390 Window fakeWindow = mock(Window.class);
391 Activity mockActivity = mock(Activity.class);
392 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
393 when(mockActivity.getWindow()).thenReturn(fakeWindow);
394 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
395
396 if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
397 WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class);
398 when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController);
399
400 SystemChromeStyle style =
401 new SystemChromeStyle(
402 null, // statusBarColor
403 Brightness.LIGHT, // statusBarIconBrightness
404 null, // systemStatusBarContrastEnforced
405 null, // systemNavigationBarColor
406 null, // systemNavigationBarIconBrightness
407 null, // systemNavigationBarDividerColor
408 null); // systemNavigationBarContrastEnforced
409
410 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
411
412 verify(fakeWindowInsetsController).setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS);
413
414 style =
415 new SystemChromeStyle(
416 null, // statusBarColor
417 Brightness.DARK, // statusBarIconBrightness
418 null, // systemStatusBarContrastEnforced
419 null, // systemNavigationBarColor
420 null, // systemNavigationBarIconBrightness
421 null, // systemNavigationBarDividerColor
422 null); // systemNavigationBarContrastEnforced
423
424 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
425
426 verify(fakeWindowInsetsController)
427 .setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS);
428 }
429 }
430
431 @SuppressWarnings("deprecation")
432 // SYSTEM_UI_FLAG_*, setSystemUiVisibility
433 @Config(sdk = API_LEVELS.API_29)
434 @Test
435 public void setSystemUiMode() {
436 View fakeDecorView = mock(View.class);
437 Window fakeWindow = mock(Window.class);
438 Activity mockActivity = mock(Activity.class);
439 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
440 when(mockActivity.getWindow()).thenReturn(fakeWindow);
441 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
442
443 if (Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
444 platformPlugin.mPlatformMessageHandler.showSystemUiMode(
445 PlatformChannel.SystemUiMode.LEAN_BACK);
446 verify(fakeDecorView)
447 .setSystemUiVisibility(
448 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
449 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
450 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
451 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
452 | View.SYSTEM_UI_FLAG_FULLSCREEN);
453
454 platformPlugin.mPlatformMessageHandler.showSystemUiMode(
455 PlatformChannel.SystemUiMode.IMMERSIVE);
456 verify(fakeDecorView)
457 .setSystemUiVisibility(
458 View.SYSTEM_UI_FLAG_IMMERSIVE
459 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
460 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
461 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
462 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
463 | View.SYSTEM_UI_FLAG_FULLSCREEN);
464
465 platformPlugin.mPlatformMessageHandler.showSystemUiMode(
466 PlatformChannel.SystemUiMode.IMMERSIVE_STICKY);
467 verify(fakeDecorView)
468 .setSystemUiVisibility(
469 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
470 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
471 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
472 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
473 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
474 | View.SYSTEM_UI_FLAG_FULLSCREEN);
475 }
476
477 if (Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
478 platformPlugin.mPlatformMessageHandler.showSystemUiMode(
479 PlatformChannel.SystemUiMode.EDGE_TO_EDGE);
480 verify(fakeDecorView)
481 .setSystemUiVisibility(
482 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
483 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
484 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
485 }
486 }
487
488 @SuppressWarnings("deprecation")
489 // SYSTEM_UI_FLAG_FULLSCREEN
490 @Test
492 ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class);
493 controller.setup();
494 Activity fakeActivity = controller.get();
495
496 PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel);
497
498 // Subscribe to system UI visibility events.
499 platformPlugin.mPlatformMessageHandler.setSystemUiChangeListener();
500
501 // Simulate system UI changed to full screen.
502 fakeActivity
503 .getWindow()
504 .getDecorView()
505 .dispatchSystemUiVisibilityChanged(View.SYSTEM_UI_FLAG_FULLSCREEN);
506
507 // No events should have been sent to the platform channel yet. They are scheduled for
508 // the next frame.
509 verify(mockPlatformChannel, never()).systemChromeChanged(anyBoolean());
510
511 // Simulate the next frame.
512 ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
513
514 // Now the platform channel should receive the event.
515 verify(mockPlatformChannel).systemChromeChanged(false);
516 }
517
518 @SuppressWarnings("deprecation")
519 // dispatchSystemUiVisibilityChanged
520 @Test
522 ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class);
523 controller.setup();
524 Activity fakeActivity = controller.get();
525 PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel);
526
527 // Subscribe to system Ui visibility events.
528 platformPlugin.mPlatformMessageHandler.setSystemUiChangeListener();
529
530 // Simulate system UI changed to *not* full screen.
531 fakeActivity.getWindow().getDecorView().dispatchSystemUiVisibilityChanged(0);
532
533 // No events should have been sent to the platform channel yet. They are scheduled for
534 // the next frame.
535 verify(mockPlatformChannel, never()).systemChromeChanged(anyBoolean());
536
537 // Simulate the next frame.
538 ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
539
540 // Now the platform channel should receive the event.
541 verify(mockPlatformChannel).systemChromeChanged(true);
542 }
543
544 @SuppressWarnings("deprecation")
545 // SYSTEM_UI_FLAG_*, setSystemUiVisibility
546 @Config(sdk = API_LEVELS.API_28)
547 @Test
549 View fakeDecorView = mock(View.class);
550 Window fakeWindow = mock(Window.class);
551 Activity mockActivity = mock(Activity.class);
552 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
553 when(mockActivity.getWindow()).thenReturn(fakeWindow);
554 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
555
556 platformPlugin.mPlatformMessageHandler.showSystemUiMode(
557 PlatformChannel.SystemUiMode.EDGE_TO_EDGE);
558 verify(fakeDecorView, never())
559 .setSystemUiVisibility(
560 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
561 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
562 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
563 }
564
565 @SuppressWarnings("deprecation")
566 // FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_NAVIGATION
567 @Config(sdk = API_LEVELS.API_29)
568 @Test
570 View fakeDecorView = mock(View.class);
571 Window fakeWindow = mock(Window.class);
572 Activity mockActivity = mock(Activity.class);
573 when(fakeWindow.getDecorView()).thenReturn(fakeDecorView);
574 when(mockActivity.getWindow()).thenReturn(fakeWindow);
575 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
576
577 SystemChromeStyle style =
578 new SystemChromeStyle(
579 0XFF000000, Brightness.LIGHT, true, 0XFFC70039, Brightness.LIGHT, 0XFF006DB3, true);
580
581 platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style);
582 verify(fakeWindow).addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
583 verify(fakeWindow)
584 .clearFlags(
585 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
586 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
587 }
588
589 @Test
591 Activity mockActivity = mock(Activity.class);
592 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
593 PlatformPlugin platformPlugin =
594 new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate);
595
596 platformPlugin.mPlatformMessageHandler.setFrameworkHandlesBack(true);
597
598 verify(mockPlatformPluginDelegate, times(1)).setFrameworkHandlesBack(true);
599 }
600
601 @Test
602 public void testPlatformPluginDelegateNull() throws Exception {
603 Activity mockActivity = mock(Activity.class);
604 PlatformPlugin platformPlugin =
605 new PlatformPlugin(mockActivity, mockPlatformChannel, null /*platformPluginDelegate*/);
606
607 try {
608 platformPlugin.mPlatformMessageHandler.setFrameworkHandlesBack(true);
609 } catch (NullPointerException e) {
610 // Not expected
611 fail("NullPointerException was thrown");
612 }
613 }
614
615 @Test
617 Activity mockActivity = mock(Activity.class);
618 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
619 PlatformPlugin platformPlugin =
620 new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate);
621
622 when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false);
623
624 platformPlugin.mPlatformMessageHandler.popSystemNavigator();
625
626 verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator();
627 verify(mockActivity, times(1)).finish();
628 }
629
630 @Test
632 Activity mockActivity = mock(Activity.class);
633 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
634 PlatformPlugin platformPlugin =
635 new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate);
636
637 when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true);
638
639 platformPlugin.mPlatformMessageHandler.popSystemNavigator();
640
641 verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator();
642 // No longer perform the default action when overridden.
643 verify(mockActivity, never()).finish();
644 }
645
646 @SuppressWarnings("deprecation")
647 // Robolectric.setupActivity.
648 // TODO(reidbaker): https://github.com/flutter/flutter/issues/133151
649 @Test
651 // Migrate to ActivityScenario by following https://github.com/robolectric/robolectric/pull/4736
652 FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
653 final AtomicBoolean onBackPressedCalled = new AtomicBoolean(false);
654 OnBackPressedCallback backCallback =
655 new OnBackPressedCallback(true) {
656 @Override
657 public void handleOnBackPressed() {
658 onBackPressedCalled.set(true);
659 }
660 };
661 activity.getOnBackPressedDispatcher().addCallback(backCallback);
662
663 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
664 when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false);
665 PlatformPlugin platformPlugin =
666 new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate);
667
668 platformPlugin.mPlatformMessageHandler.popSystemNavigator();
669
670 verify(activity, never()).finish();
671 verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator();
672 assertTrue(onBackPressedCalled.get());
673 }
674
675 @SuppressWarnings("deprecation")
676 // Robolectric.setupActivity.
677 // TODO(reidbaker): https://github.com/flutter/flutter/issues/133151
678 @Test
680 FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
681 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
682 when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true);
683 PlatformPlugin platformPlugin =
684 new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate);
685
686 platformPlugin.mPlatformMessageHandler.popSystemNavigator();
687
688 verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator();
689 // No longer perform the default action when overridden.
690 verify(activity, never()).finish();
691 }
692
693 @Test
695 FragmentActivity mockFragmentActivity = mock(FragmentActivity.class);
696 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
697 when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false);
698 PlatformPlugin platformPlugin =
699 new PlatformPlugin(mockFragmentActivity, mockPlatformChannel, mockPlatformPluginDelegate);
700
701 platformPlugin.mPlatformMessageHandler.setPreferredOrientations(0);
702
703 verify(mockFragmentActivity, times(1)).setRequestedOrientation(0);
704 }
705
706 @Test
708 Activity mockActivity = mock(Activity.class);
709 PlatformChannel mockPlatformChannel = mock(PlatformChannel.class);
710 PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel);
711
712 platformPlugin.mPlatformMessageHandler.popSystemNavigator();
713
714 verify(mockActivity, times(1)).finish();
715 }
716
717 @Test
719 Activity mockActivity = mock(Activity.class);
720 PlatformChannel mockPlatformChannel = mock(PlatformChannel.class);
721 PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class);
722 PlatformPlugin platformPlugin =
723 new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate);
724
725 // Mock Intent.createChooser (in real application it opens a chooser where the user can
726 // select which application will be used to share the selected text).
727 Intent choosenIntent = new Intent();
728 MockedStatic<Intent> intentClass = mockStatic(Intent.class);
729 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
730 intentClass
731 .when(() -> Intent.createChooser(intentCaptor.capture(), any()))
732 .thenReturn(choosenIntent);
733
734 final String expectedContent = "Flutter";
735 platformPlugin.mPlatformMessageHandler.share(expectedContent);
736
737 // Activity.startActivity should have been called.
738 verify(mockActivity, times(1)).startActivity(choosenIntent);
739
740 // The intent action created by the plugin and passed to Intent.createChooser should be
741 // 'Intent.ACTION_SEND'.
742 Intent sendToIntent = intentCaptor.getValue();
743 assertEquals(sendToIntent.getAction(), Intent.ACTION_SEND);
744 assertEquals(sendToIntent.getType(), "text/plain");
745 assertEquals(sendToIntent.getStringExtra(Intent.EXTRA_TEXT), expectedContent);
746 }
747}
static SkISize times(const SkISize &size, float factor)
static void fail(const SkString &err)
Definition DM.cpp:234
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler