Flutter Engine
The Flutter Engine
FlutterView.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.view;
6
7import static io.flutter.Build.API_LEVELS;
8
9import android.annotation.SuppressLint;
10import android.annotation.TargetApi;
11import android.app.Activity;
12import android.content.Context;
13import android.content.res.Configuration;
14import android.graphics.Bitmap;
15import android.graphics.Insets;
16import android.graphics.PixelFormat;
17import android.graphics.SurfaceTexture;
18import android.os.Build;
19import android.os.Handler;
20import android.text.format.DateFormat;
21import android.util.AttributeSet;
22import android.util.SparseArray;
23import android.view.DisplayCutout;
24import android.view.KeyEvent;
25import android.view.MotionEvent;
26import android.view.PointerIcon;
27import android.view.Surface;
28import android.view.SurfaceHolder;
29import android.view.SurfaceView;
30import android.view.View;
31import android.view.ViewConfiguration;
32import android.view.ViewStructure;
33import android.view.WindowInsets;
34import android.view.WindowManager;
35import android.view.accessibility.AccessibilityManager;
36import android.view.accessibility.AccessibilityNodeProvider;
37import android.view.autofill.AutofillValue;
38import android.view.inputmethod.EditorInfo;
39import android.view.inputmethod.InputConnection;
40import android.view.inputmethod.InputMethodManager;
41import android.window.BackEvent;
42import androidx.annotation.NonNull;
43import androidx.annotation.RequiresApi;
44import androidx.annotation.UiThread;
45import io.flutter.Log;
46import io.flutter.app.FlutterPluginRegistry;
47import io.flutter.embedding.android.AndroidTouchProcessor;
48import io.flutter.embedding.android.KeyboardManager;
49import io.flutter.embedding.engine.dart.DartExecutor;
50import io.flutter.embedding.engine.renderer.FlutterRenderer;
51import io.flutter.embedding.engine.renderer.SurfaceTextureWrapper;
52import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
53import io.flutter.embedding.engine.systemchannels.BackGestureChannel;
54import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
55import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
56import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
57import io.flutter.embedding.engine.systemchannels.NavigationChannel;
58import io.flutter.embedding.engine.systemchannels.PlatformChannel;
59import io.flutter.embedding.engine.systemchannels.SettingsChannel;
60import io.flutter.embedding.engine.systemchannels.SystemChannel;
61import io.flutter.embedding.engine.systemchannels.TextInputChannel;
62import io.flutter.plugin.common.ActivityLifecycleListener;
63import io.flutter.plugin.common.BinaryMessenger;
64import io.flutter.plugin.editing.TextInputPlugin;
65import io.flutter.plugin.localization.LocalizationPlugin;
66import io.flutter.plugin.mouse.MouseCursorPlugin;
67import io.flutter.plugin.platform.PlatformPlugin;
68import io.flutter.plugin.platform.PlatformViewsController;
69import io.flutter.util.ViewUtils;
70import java.nio.ByteBuffer;
71import java.util.ArrayList;
72import java.util.List;
73import java.util.concurrent.atomic.AtomicLong;
74
75/**
76 * Deprecated Android view containing a Flutter app.
77 *
78 * @deprecated {@link io.flutter.embedding.android.FlutterView} is the new API that now replaces
79 * this class. See https://flutter.dev/go/android-project-migration for more migration details.
80 */
81@Deprecated
82public class FlutterView extends SurfaceView
83 implements BinaryMessenger,
85 MouseCursorPlugin.MouseCursorViewDelegate,
86 KeyboardManager.ViewDelegate {
87 /**
88 * Interface for those objects that maintain and expose a reference to a {@code FlutterView} (such
89 * as a full-screen Flutter activity).
90 *
91 * <p>This indirection is provided to support applications that use an activity other than {@link
92 * io.flutter.app.FlutterActivity} (e.g. Android v4 support library's {@code FragmentActivity}).
93 * It allows Flutter plugins to deal in this interface and not require that the activity be a
94 * subclass of {@code FlutterActivity}.
95 */
96 public interface Provider {
97 /**
98 * Returns a reference to the Flutter view maintained by this object. This may be {@code null}.
99 *
100 * @return a reference to the Flutter view maintained by this object.
101 */
103 }
104
105 private static final String TAG = "FlutterView";
106
107 static final class ViewportMetrics {
108 float devicePixelRatio = 1.0f;
124 }
125
126 private final DartExecutor dartExecutor;
127 private final FlutterRenderer flutterRenderer;
128 private final NavigationChannel navigationChannel;
129 private final BackGestureChannel backGestureChannel;
130 private final LifecycleChannel lifecycleChannel;
131 private final LocalizationChannel localizationChannel;
132 private final PlatformChannel platformChannel;
133 private final SettingsChannel settingsChannel;
134 private final SystemChannel systemChannel;
135 private final InputMethodManager mImm;
136 private final TextInputPlugin mTextInputPlugin;
137 private final LocalizationPlugin mLocalizationPlugin;
138 private final MouseCursorPlugin mMouseCursorPlugin;
139 private final KeyboardManager mKeyboardManager;
140 private final AndroidTouchProcessor androidTouchProcessor;
141 private AccessibilityBridge mAccessibilityNodeProvider;
142 private final SurfaceHolder.Callback mSurfaceCallback;
143 private final ViewportMetrics mMetrics;
144 private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
145 private final List<FirstFrameListener> mFirstFrameListeners;
146 private final AtomicLong nextTextureId = new AtomicLong(0L);
147 private FlutterNativeView mNativeView;
148 private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not
149 private boolean didRenderFirstFrame = false;
150
151 private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener =
152 new AccessibilityBridge.OnAccessibilityChangeListener() {
153 @Override
154 public void onAccessibilityChanged(
155 boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
156 resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
157 }
158 };
159
160 public FlutterView(Context context) {
161 this(context, null);
162 }
163
164 public FlutterView(Context context, AttributeSet attrs) {
165 this(context, attrs, null);
166 }
167
168 public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
169 super(context, attrs);
170
171 Activity activity = ViewUtils.getActivity(getContext());
172 if (activity == null) {
173 throw new IllegalArgumentException("Bad context");
174 }
175
176 if (nativeView == null) {
177 mNativeView = new FlutterNativeView(activity.getApplicationContext());
178 } else {
179 mNativeView = nativeView;
180 }
181
182 dartExecutor = mNativeView.getDartExecutor();
183 flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
184 mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().getIsSoftwareRenderingEnabled();
185 mMetrics = new ViewportMetrics();
186 mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
187 mMetrics.physicalTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
188 setFocusable(true);
189 setFocusableInTouchMode(true);
190
191 mNativeView.attachViewAndActivity(this, activity);
192
193 mSurfaceCallback =
194 new SurfaceHolder.Callback() {
195 @Override
196 public void surfaceCreated(SurfaceHolder holder) {
198 mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
199 }
200
201 @Override
202 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
205 }
206
207 @Override
208 public void surfaceDestroyed(SurfaceHolder holder) {
210 mNativeView.getFlutterJNI().onSurfaceDestroyed();
211 }
212 };
213 getHolder().addCallback(mSurfaceCallback);
214
215 mActivityLifecycleListeners = new ArrayList<>();
216 mFirstFrameListeners = new ArrayList<>();
217
218 // Create all platform channels
219 navigationChannel = new NavigationChannel(dartExecutor);
220 backGestureChannel = new BackGestureChannel(dartExecutor);
221 lifecycleChannel = new LifecycleChannel(dartExecutor);
222 localizationChannel = new LocalizationChannel(dartExecutor);
223 platformChannel = new PlatformChannel(dartExecutor);
224 systemChannel = new SystemChannel(dartExecutor);
225 settingsChannel = new SettingsChannel(dartExecutor);
226
227 // Create and set up plugins
228 PlatformPlugin platformPlugin = new PlatformPlugin(activity, platformChannel);
231 @Override
232 public void onPostResume() {
233 platformPlugin.updateSystemUiOverlays();
234 }
235 });
236 mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
237 PlatformViewsController platformViewsController =
239 mTextInputPlugin =
240 new TextInputPlugin(this, new TextInputChannel(dartExecutor), platformViewsController);
241 mKeyboardManager = new KeyboardManager(this);
242
243 if (Build.VERSION.SDK_INT >= API_LEVELS.API_24) {
244 mMouseCursorPlugin = new MouseCursorPlugin(this, new MouseCursorChannel(dartExecutor));
245 } else {
246 mMouseCursorPlugin = null;
247 }
248 mLocalizationPlugin = new LocalizationPlugin(context, localizationChannel);
249 androidTouchProcessor =
250 new AndroidTouchProcessor(flutterRenderer, /*trackMotionEvents=*/ false);
251 platformViewsController.attachToFlutterRenderer(flutterRenderer);
252 mNativeView
255 .attachTextInputPlugin(mTextInputPlugin);
256 mNativeView.getFlutterJNI().setLocalizationPlugin(mLocalizationPlugin);
257
258 // Send initial platform information to Dart
259 mLocalizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
260 sendUserPlatformSettingsToDart();
261 }
262
263 @NonNull
265 return dartExecutor;
266 }
267
268 @Override
269 public boolean dispatchKeyEvent(KeyEvent event) {
270 Log.e(TAG, "dispatchKeyEvent: " + event.toString());
271 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
272 // Tell Android to start tracking this event.
273 getKeyDispatcherState().startTracking(event, this);
274 } else if (event.getAction() == KeyEvent.ACTION_UP) {
275 // Stop tracking the event.
276 getKeyDispatcherState().handleUpEvent(event);
277 }
278 // If the key processor doesn't handle it, then send it on to the
279 // superclass. The key processor will typically handle all events except
280 // those where it has re-dispatched the event after receiving a reply from
281 // the framework that the framework did not handle it.
282 return (isAttached() && mKeyboardManager.handleEvent(event)) || super.dispatchKeyEvent(event);
283 }
284
286 return mNativeView;
287 }
288
290 return mNativeView.getPluginRegistry();
291 }
292
293 public String getLookupKeyForAsset(String asset) {
294 return FlutterMain.getLookupKeyForAsset(asset);
295 }
296
297 public String getLookupKeyForAsset(String asset, String packageName) {
298 return FlutterMain.getLookupKeyForAsset(asset, packageName);
299 }
300
302 mActivityLifecycleListeners.add(listener);
303 }
304
305 public void onStart() {
306 lifecycleChannel.appIsInactive();
307 }
308
309 public void onPause() {
310 lifecycleChannel.appIsInactive();
311 }
312
313 public void onPostResume() {
314 for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
315 listener.onPostResume();
316 }
317 lifecycleChannel.appIsResumed();
318 }
319
320 public void onStop() {
321 lifecycleChannel.appIsPaused();
322 }
323
324 public void onMemoryPressure() {
326 systemChannel.sendMemoryPressureWarning();
327 }
328
329 /**
330 * Returns true if the Flutter experience associated with this {@code FlutterView} has rendered
331 * its first frame, or false otherwise.
332 */
333 public boolean hasRenderedFirstFrame() {
334 return didRenderFirstFrame;
335 }
336
337 /**
338 * Provide a listener that will be called once when the FlutterView renders its first frame to the
339 * underlaying SurfaceView.
340 */
342 mFirstFrameListeners.add(listener);
343 }
344
345 /** Remove an existing first frame listener. */
347 mFirstFrameListeners.remove(listener);
348 }
349
350 @Override
352
353 @Override
355
356 /**
357 * Reverts this back to the {@link SurfaceView} defaults, at the back of its window and opaque.
358 */
360 setZOrderOnTop(false);
361 getHolder().setFormat(PixelFormat.OPAQUE);
362 }
363
364 public void setInitialRoute(String route) {
365 navigationChannel.setInitialRoute(route);
366 }
367
368 public void pushRoute(String route) {
369 navigationChannel.pushRoute(route);
370 }
371
372 public void popRoute() {
373 navigationChannel.popRoute();
374 }
375
376 @TargetApi(API_LEVELS.API_34)
377 @RequiresApi(API_LEVELS.API_34)
378 public void startBackGesture(@NonNull BackEvent backEvent) {
379 backGestureChannel.startBackGesture(backEvent);
380 }
381
382 @TargetApi(API_LEVELS.API_34)
383 @RequiresApi(API_LEVELS.API_34)
384 public void updateBackGestureProgress(@NonNull BackEvent backEvent) {
385 backGestureChannel.updateBackGestureProgress(backEvent);
386 }
387
388 @TargetApi(API_LEVELS.API_34)
389 @RequiresApi(API_LEVELS.API_34)
390 public void commitBackGesture() {
391 backGestureChannel.commitBackGesture();
392 }
393
394 @TargetApi(API_LEVELS.API_34)
395 @RequiresApi(API_LEVELS.API_34)
396 public void cancelBackGesture() {
397 backGestureChannel.cancelBackGesture();
398 }
399
400 private void sendUserPlatformSettingsToDart() {
401 // Lookup the current brightness of the Android OS.
402 boolean isNightModeOn =
403 (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
404 == Configuration.UI_MODE_NIGHT_YES;
406 isNightModeOn
409
410 settingsChannel
411 .startMessage()
412 .setTextScaleFactor(getResources().getConfiguration().fontScale)
413 .setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
414 .setPlatformBrightness(brightness)
415 .send();
416 }
417
418 @Override
419 protected void onConfigurationChanged(Configuration newConfig) {
420 super.onConfigurationChanged(newConfig);
421 mLocalizationPlugin.sendLocalesToFlutter(newConfig);
422 sendUserPlatformSettingsToDart();
423 }
424
426 return mMetrics.devicePixelRatio;
427 }
428
430 if (!isAttached()) return null;
431 getHolder().removeCallback(mSurfaceCallback);
432 mNativeView.detachFromFlutterView();
433
434 FlutterNativeView view = mNativeView;
435 mNativeView = null;
436 return view;
437 }
438
439 public void destroy() {
440 if (!isAttached()) return;
441
442 getHolder().removeCallback(mSurfaceCallback);
443 releaseAccessibilityNodeProvider();
444
445 mNativeView.destroy();
446 mNativeView = null;
447 }
448
449 @Override
450 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
451 return mTextInputPlugin.createInputConnection(this, mKeyboardManager, outAttrs);
452 }
453
454 @Override
455 public boolean checkInputConnectionProxy(View view) {
456 return mNativeView
460 }
461
462 @Override
463 public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
464 super.onProvideAutofillVirtualStructure(structure, flags);
465 mTextInputPlugin.onProvideAutofillVirtualStructure(structure, flags);
466 }
467
468 @Override
469 public void autofill(SparseArray<AutofillValue> values) {
470 mTextInputPlugin.autofill(values);
471 }
472
473 @Override
474 public boolean onTouchEvent(MotionEvent event) {
475 if (!isAttached()) {
476 return super.onTouchEvent(event);
477 }
478
479 requestUnbufferedDispatch(event);
480
481 return androidTouchProcessor.onTouchEvent(event);
482 }
483
484 @Override
485 public boolean onHoverEvent(MotionEvent event) {
486 if (!isAttached()) {
487 return super.onHoverEvent(event);
488 }
489
490 boolean handled = mAccessibilityNodeProvider.onAccessibilityHoverEvent(event);
491 if (!handled) {
492 // TODO(ianh): Expose hover events to the platform,
493 // implementing ADD, REMOVE, etc.
494 }
495 return handled;
496 }
497
498 /**
499 * Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover,
500 * track pad touches, scroll wheel movements, etc.
501 *
502 * <p>Flutter handles all of its own gesture detection and processing, therefore this method
503 * forwards all {@link MotionEvent} data from Android to Flutter.
504 */
505 @Override
506 public boolean onGenericMotionEvent(MotionEvent event) {
507 boolean handled =
508 isAttached() && androidTouchProcessor.onGenericMotionEvent(event, getContext());
509 return handled ? true : super.onGenericMotionEvent(event);
510 }
511
512 @Override
513 protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
514 mMetrics.physicalWidth = width;
515 mMetrics.physicalHeight = height;
516 updateViewportMetrics();
517 super.onSizeChanged(width, height, oldWidth, oldHeight);
518 }
519
520 // TODO(garyq): Add support for notch cutout API
521 // Decide if we want to zero the padding of the sides. When in Landscape orientation,
522 // android may decide to place the software navigation bars on the side. When the nav
523 // bar is hidden, the reported insets should be removed to prevent extra useless space
524 // on the sides.
525 private enum ZeroSides {
526 NONE,
527 LEFT,
528 RIGHT,
529 BOTH
530 }
531
532 private ZeroSides calculateShouldZeroSides() {
533 // We get both orientation and rotation because rotation is all 4
534 // rotations relative to default rotation while orientation is portrait
535 // or landscape. By combining both, we can obtain a more precise measure
536 // of the rotation.
537 Context context = getContext();
538 int orientation = context.getResources().getConfiguration().orientation;
539 int rotation =
540 ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
541 .getDefaultDisplay()
542 .getRotation();
543
544 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
545 if (rotation == Surface.ROTATION_90) {
546 return ZeroSides.RIGHT;
547 } else if (rotation == Surface.ROTATION_270) {
548 // In android API >= 23, the nav bar always appears on the "bottom" (USB) side.
549 return Build.VERSION.SDK_INT >= API_LEVELS.API_23 ? ZeroSides.LEFT : ZeroSides.RIGHT;
550 }
551 // Ambiguous orientation due to landscape left/right default. Zero both sides.
552 else if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
553 return ZeroSides.BOTH;
554 }
555 }
556 // Square orientation deprecated in API 16, we will not check for it and return false
557 // to be safe and not remove any unique padding for the devices that do use it.
558 return ZeroSides.NONE;
559 }
560
561 // TODO(garyq): Use new Android R getInsets API
562 // TODO(garyq): The keyboard detection may interact strangely with
563 // https://github.com/flutter/flutter/issues/22061
564
565 // Uses inset heights and screen heights as a heuristic to determine if the insets should
566 // be padded. When the on-screen keyboard is detected, we want to include the full inset
567 // but when the inset is just the hidden nav bar, we want to provide a zero inset so the space
568 // can be used.
569
570 private int guessBottomKeyboardInset(WindowInsets insets) {
571 int screenHeight = getRootView().getHeight();
572 // Magic number due to this being a heuristic. This should be replaced, but we have not
573 // found a clean way to do it yet (Sept. 2018)
574 final double keyboardHeightRatioHeuristic = 0.18;
575 if (insets.getSystemWindowInsetBottom() < screenHeight * keyboardHeightRatioHeuristic) {
576 // Is not a keyboard, so return zero as inset.
577 return 0;
578 } else {
579 // Is a keyboard, so return the full inset.
580 return insets.getSystemWindowInsetBottom();
581 }
582 }
583
584 // This callback is not present in API < 20, which means lower API devices will see
585 // the wider than expected padding when the status and navigation bars are hidden.
586 // The annotations to suppress "InlinedApi" and "NewApi" lints prevent lint warnings
587 // caused by usage of Android Q APIs. These calls are safe because they are
588 // guarded.
589 @Override
590 @SuppressLint({"InlinedApi", "NewApi"})
591 public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
592 // getSystemGestureInsets() was introduced in API 29 and immediately deprecated in 30.
593 if (Build.VERSION.SDK_INT == API_LEVELS.API_29) {
594 Insets systemGestureInsets = insets.getSystemGestureInsets();
595 mMetrics.systemGestureInsetTop = systemGestureInsets.top;
596 mMetrics.systemGestureInsetRight = systemGestureInsets.right;
597 mMetrics.systemGestureInsetBottom = systemGestureInsets.bottom;
598 mMetrics.systemGestureInsetLeft = systemGestureInsets.left;
599 }
600
601 boolean statusBarVisible = (SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility()) == 0;
602 boolean navigationBarVisible =
603 (SYSTEM_UI_FLAG_HIDE_NAVIGATION & getWindowSystemUiVisibility()) == 0;
604
605 if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
606 int mask = 0;
607 if (navigationBarVisible) {
608 mask = mask | android.view.WindowInsets.Type.navigationBars();
609 }
610 if (statusBarVisible) {
611 mask = mask | android.view.WindowInsets.Type.statusBars();
612 }
613 Insets uiInsets = insets.getInsets(mask);
614 mMetrics.physicalViewPaddingTop = uiInsets.top;
615 mMetrics.physicalViewPaddingRight = uiInsets.right;
616 mMetrics.physicalViewPaddingBottom = uiInsets.bottom;
617 mMetrics.physicalViewPaddingLeft = uiInsets.left;
618
619 Insets imeInsets = insets.getInsets(android.view.WindowInsets.Type.ime());
620 mMetrics.physicalViewInsetTop = imeInsets.top;
621 mMetrics.physicalViewInsetRight = imeInsets.right;
622 mMetrics.physicalViewInsetBottom = imeInsets.bottom; // Typically, only bottom is non-zero
623 mMetrics.physicalViewInsetLeft = imeInsets.left;
624
625 Insets systemGestureInsets =
626 insets.getInsets(android.view.WindowInsets.Type.systemGestures());
627 mMetrics.systemGestureInsetTop = systemGestureInsets.top;
628 mMetrics.systemGestureInsetRight = systemGestureInsets.right;
629 mMetrics.systemGestureInsetBottom = systemGestureInsets.bottom;
630 mMetrics.systemGestureInsetLeft = systemGestureInsets.left;
631
632 // TODO(garyq): Expose the full rects of the display cutout.
633
634 // Take the max of the display cutout insets and existing padding to merge them
635 DisplayCutout cutout = insets.getDisplayCutout();
636 if (cutout != null) {
637 Insets waterfallInsets = cutout.getWaterfallInsets();
638 mMetrics.physicalViewPaddingTop =
639 Math.max(
640 Math.max(mMetrics.physicalViewPaddingTop, waterfallInsets.top),
641 cutout.getSafeInsetTop());
642 mMetrics.physicalViewPaddingRight =
643 Math.max(
644 Math.max(mMetrics.physicalViewPaddingRight, waterfallInsets.right),
645 cutout.getSafeInsetRight());
646 mMetrics.physicalViewPaddingBottom =
647 Math.max(
648 Math.max(mMetrics.physicalViewPaddingBottom, waterfallInsets.bottom),
649 cutout.getSafeInsetBottom());
650 mMetrics.physicalViewPaddingLeft =
651 Math.max(
652 Math.max(mMetrics.physicalViewPaddingLeft, waterfallInsets.left),
653 cutout.getSafeInsetLeft());
654 }
655 } else {
656 // We zero the left and/or right sides to prevent the padding the
657 // navigation bar would have caused.
658 ZeroSides zeroSides = ZeroSides.NONE;
659 if (!navigationBarVisible) {
660 zeroSides = calculateShouldZeroSides();
661 }
662
663 // Status bar (top), navigation bar (bottom) and left/right system insets should
664 // partially obscure the content (padding).
665 mMetrics.physicalViewPaddingTop = statusBarVisible ? insets.getSystemWindowInsetTop() : 0;
666 mMetrics.physicalViewPaddingRight =
667 zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH
668 ? 0
669 : insets.getSystemWindowInsetRight();
670 mMetrics.physicalViewPaddingBottom =
671 navigationBarVisible && guessBottomKeyboardInset(insets) == 0
672 ? insets.getSystemWindowInsetBottom()
673 : 0;
674 mMetrics.physicalViewPaddingLeft =
675 zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH
676 ? 0
677 : insets.getSystemWindowInsetLeft();
678
679 // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
680 mMetrics.physicalViewInsetTop = 0;
681 mMetrics.physicalViewInsetRight = 0;
682 mMetrics.physicalViewInsetBottom = guessBottomKeyboardInset(insets);
683 mMetrics.physicalViewInsetLeft = 0;
684 }
685
686 updateViewportMetrics();
687 return super.onApplyWindowInsets(insets);
688 }
689
690 private boolean isAttached() {
691 return mNativeView != null && mNativeView.isAttached();
692 }
693
695 if (!isAttached()) throw new AssertionError("Platform view is not attached");
696 }
697
698 private void preRun() {
700 }
701
703 if (mAccessibilityNodeProvider != null) {
704 mAccessibilityNodeProvider.reset();
705 }
706 }
707
708 private void postRun() {}
709
712 preRun();
713 mNativeView.runFromBundle(args);
714 postRun();
715 }
716
717 /**
718 * Return the most recent frame as a bitmap.
719 *
720 * @return A bitmap.
721 */
722 public Bitmap getBitmap() {
724 return mNativeView.getFlutterJNI().getBitmap();
725 }
726
727 private void updateViewportMetrics() {
728 if (!isAttached()) return;
729 mNativeView
732 mMetrics.devicePixelRatio,
733 mMetrics.physicalWidth,
734 mMetrics.physicalHeight,
735 mMetrics.physicalViewPaddingTop,
739 mMetrics.physicalViewInsetTop,
740 mMetrics.physicalViewInsetRight,
742 mMetrics.physicalViewInsetLeft,
743 mMetrics.systemGestureInsetTop,
746 mMetrics.systemGestureInsetLeft,
747 mMetrics.physicalTouchSlop,
748 new int[0],
749 new int[0],
750 new int[0]);
751 }
752
753 // Called by FlutterNativeView to notify first Flutter frame rendered.
754 public void onFirstFrame() {
755 didRenderFirstFrame = true;
756
757 // Allow listeners to remove themselves when they are called.
758 List<FirstFrameListener> listeners = new ArrayList<>(mFirstFrameListeners);
759 for (FirstFrameListener listener : listeners) {
760 listener.onFirstFrame();
761 }
762 }
763
764 @Override
765 protected void onAttachedToWindow() {
766 super.onAttachedToWindow();
767
768 PlatformViewsController platformViewsController =
770 mAccessibilityNodeProvider =
772 this,
773 new AccessibilityChannel(dartExecutor, getFlutterNativeView().getFlutterJNI()),
774 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
775 getContext().getContentResolver(),
776 platformViewsController);
777 mAccessibilityNodeProvider.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
778
779 resetWillNotDraw(
780 mAccessibilityNodeProvider.isAccessibilityEnabled(),
781 mAccessibilityNodeProvider.isTouchExplorationEnabled());
782 }
783
784 @Override
785 protected void onDetachedFromWindow() {
786 super.onDetachedFromWindow();
787 releaseAccessibilityNodeProvider();
788 }
789
790 // TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise
791 // add comments.
792 private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
793 if (!mIsSoftwareRenderingEnabled) {
794 setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled));
795 } else {
796 setWillNotDraw(false);
797 }
798 }
799
800 @Override
801 public AccessibilityNodeProvider getAccessibilityNodeProvider() {
802 if (mAccessibilityNodeProvider != null && mAccessibilityNodeProvider.isAccessibilityEnabled()) {
803 return mAccessibilityNodeProvider;
804 } else {
805 // TODO(goderbauer): when a11y is off this should return a one-off snapshot of
806 // the a11y
807 // tree.
808 return null;
809 }
810 }
811
812 private void releaseAccessibilityNodeProvider() {
813 if (mAccessibilityNodeProvider != null) {
814 mAccessibilityNodeProvider.release();
815 mAccessibilityNodeProvider = null;
816 }
817 }
818
819 // -------- Start: Mouse -------
820
821 @Override
822 @TargetApi(API_LEVELS.API_24)
823 @RequiresApi(API_LEVELS.API_24)
824 @NonNull
825 public PointerIcon getSystemPointerIcon(int type) {
826 return PointerIcon.getSystemIcon(getContext(), type);
827 }
828
829 // -------- End: Mouse -------
830
831 // -------- Start: Keyboard -------
832
833 @Override
835 return this;
836 }
837
838 @Override
839 public boolean onTextInputKeyEvent(@NonNull KeyEvent keyEvent) {
840 return mTextInputPlugin.handleKeyEvent(keyEvent);
841 }
842
843 @Override
844 public void redispatch(@NonNull KeyEvent keyEvent) {
845 getRootView().dispatchKeyEvent(keyEvent);
846 }
847
848 // -------- End: Keyboard -------
849
850 @Override
851 @UiThread
853 return null;
854 }
855
856 @Override
857 @UiThread
858 public void send(String channel, ByteBuffer message) {
859 send(channel, message, null);
860 }
861
862 @Override
863 @UiThread
864 public void send(String channel, ByteBuffer message, BinaryReply callback) {
865 if (!isAttached()) {
866 Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
867 return;
868 }
869 mNativeView.send(channel, message, callback);
870 }
871
872 @Override
873 @UiThread
874 public void setMessageHandler(@NonNull String channel, @NonNull BinaryMessageHandler handler) {
875 mNativeView.setMessageHandler(channel, handler);
876 }
877
878 @Override
879 @UiThread
880 public void setMessageHandler(
881 @NonNull String channel,
882 @NonNull BinaryMessageHandler handler,
883 @NonNull TaskQueue taskQueue) {
884 mNativeView.setMessageHandler(channel, handler, taskQueue);
885 }
886
887 /** Listener will be called on the Android UI thread once when Flutter renders the first frame. */
888 public interface FirstFrameListener {
890 }
891
892 @Override
893 @NonNull
894 public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() {
895 final SurfaceTexture surfaceTexture = new SurfaceTexture(0);
896 return registerSurfaceTexture(surfaceTexture);
897 }
898
899 @Override
900 @NonNull
902 throw new UnsupportedOperationException("Image textures are not supported in this mode.");
903 }
904
905 @Override
907 throw new UnsupportedOperationException(
908 "SurfaceProducer textures are not supported in this mode.");
909 }
910
911 @Override
912 @NonNull
913 public TextureRegistry.SurfaceTextureEntry registerSurfaceTexture(
914 @NonNull SurfaceTexture surfaceTexture) {
915 surfaceTexture.detachFromGLContext();
916 final SurfaceTextureRegistryEntry entry =
917 new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture);
918 mNativeView.getFlutterJNI().registerTexture(entry.id(), entry.textureWrapper());
919 return entry;
920 }
921
923 private final long id;
924 private final SurfaceTextureWrapper textureWrapper;
925 private boolean released;
926
928 this.id = id;
929 this.textureWrapper = new SurfaceTextureWrapper(surfaceTexture);
930
931 // The callback relies on being executed on the UI thread (unsynchronised read of
932 // mNativeView
933 // and also the engine code check for platform thread in
934 // Shell::OnPlatformViewMarkTextureFrameAvailable),
935 // so we explicitly pass a Handler for the current thread.
936 this.surfaceTexture().setOnFrameAvailableListener(onFrameListener, new Handler());
937 }
938
939 private SurfaceTexture.OnFrameAvailableListener onFrameListener =
940 new SurfaceTexture.OnFrameAvailableListener() {
941 @Override
942 public void onFrameAvailable(SurfaceTexture texture) {
943 if (released || mNativeView == null) {
944 // Even though we make sure to unregister the callback before releasing, as of Android
945 // O
946 // SurfaceTexture has a data race when accessing the callback, so the callback may
947 // still be called by a stale reference after released==true and mNativeView==null.
948 return;
949 }
950
951 mNativeView
954 }
955 };
956
958 return textureWrapper;
959 }
960
961 @NonNull
962 @Override
963 public SurfaceTexture surfaceTexture() {
964 return textureWrapper.surfaceTexture();
965 }
966
967 @Override
968 public long id() {
969 return id;
970 }
971
972 @Override
973 public void release() {
974 if (released) {
975 return;
976 }
977 released = true;
978
979 // The ordering of the next 3 calls is important:
980 // First we remove the frame listener, then we release the SurfaceTexture, and only after we
981 // unregister
982 // the texture which actually deletes the GL texture.
983
984 // Otherwise onFrameAvailableListener might be called after mNativeView==null
985 // (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable.
986 surfaceTexture().setOnFrameAvailableListener(null);
987 textureWrapper.release();
988 mNativeView.getFlutterJNI().unregisterTexture(id);
989 }
990 }
991}
const char * options
GLenum type
static final int API_34
Definition: Build.java:24
static final int API_29
Definition: Build.java:19
static final int API_30
Definition: Build.java:20
static final int API_24
Definition: Build.java:14
static void d(@NonNull String tag, @NonNull String message)
Definition: Log.java:64
static void e(@NonNull String tag, @NonNull String message)
Definition: Log.java:84
PlatformViewsController getPlatformViewsController()
boolean onGenericMotionEvent(@NonNull MotionEvent event, @NonNull Context context)
boolean handleEvent(@NonNull KeyEvent keyEvent)
void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper)
void onSurfaceCreated(@NonNull Surface surface)
void setLocalizationPlugin(@Nullable LocalizationPlugin localizationPlugin)
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)
void unregisterTexture(long textureId)
void onSurfaceChanged(int width, int height)
void markTextureFrameAvailable(long textureId)
boolean handleKeyEvent(@NonNull KeyEvent keyEvent)
InputConnection createInputConnection( @NonNull View view, @NonNull KeyboardManager keyboardManager, @NonNull EditorInfo outAttrs)
void onProvideAutofillVirtualStructure(@NonNull ViewStructure structure, int flags)
void autofill(@NonNull SparseArray< AutofillValue > values)
void sendLocalesToFlutter(@NonNull Configuration config)
void attachToFlutterRenderer(@NonNull FlutterRenderer flutterRenderer)
void attachTextInputPlugin(@NonNull TextInputPlugin textInputPlugin)
static Activity getActivity(@Nullable Context context)
Definition: ViewUtils.java:47
static String getLookupKeyForAsset(@NonNull String asset)
void setMessageHandler(String channel, BinaryMessageHandler handler)
void send(String channel, ByteBuffer message)
FlutterPluginRegistry getPluginRegistry()
void attachViewAndActivity(FlutterView flutterView, Activity activity)
void runFromBundle(FlutterRunArguments args)
SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture)
void addFirstFrameListener(FirstFrameListener listener)
boolean onHoverEvent(MotionEvent event)
void setInitialRoute(String route)
void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
void removeFirstFrameListener(FirstFrameListener listener)
PointerIcon getSystemPointerIcon(int type)
FlutterNativeView getFlutterNativeView()
String getLookupKeyForAsset(String asset)
void setMessageHandler( @NonNull String channel, @NonNull BinaryMessageHandler handler, @NonNull TaskQueue taskQueue)
void onConfigurationChanged(Configuration newConfig)
void redispatch(@NonNull KeyEvent keyEvent)
final WindowInsets onApplyWindowInsets(WindowInsets insets)
void updateBackGestureProgress(@NonNull BackEvent backEvent)
void setMessageHandler(@NonNull String channel, @NonNull BinaryMessageHandler handler)
FlutterView(Context context, AttributeSet attrs)
void pushRoute(String route)
ImageTextureEntry createImageTexture()
FlutterView(Context context)
boolean dispatchKeyEvent(KeyEvent event)
boolean checkInputConnectionProxy(View view)
void send(String channel, ByteBuffer message)
FlutterNativeView detach()
DartExecutor getDartExecutor()
String getLookupKeyForAsset(String asset, String packageName)
boolean onGenericMotionEvent(MotionEvent event)
BinaryMessenger getBinaryMessenger()
void runFromBundle(FlutterRunArguments args)
TaskQueue makeBackgroundTaskQueue(TaskQueueOptions options)
TextureRegistry.SurfaceTextureEntry registerSurfaceTexture( @NonNull SurfaceTexture surfaceTexture)
InputConnection onCreateInputConnection(EditorInfo outAttrs)
SurfaceProducer createSurfaceProducer()
void addActivityLifecycleListener(ActivityLifecycleListener listener)
void send(String channel, ByteBuffer message, BinaryReply callback)
void onProvideAutofillVirtualStructure(ViewStructure structure, int flags)
void startBackGesture(@NonNull BackEvent backEvent)
TextureRegistry.SurfaceTextureEntry createSurfaceTexture()
boolean onTextInputKeyEvent(@NonNull KeyEvent keyEvent)
AccessibilityNodeProvider getAccessibilityNodeProvider()
FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView)
boolean onTouchEvent(MotionEvent event)
FlutterPluginRegistry getPluginRegistry()
void autofill(SparseArray< AutofillValue > values)
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
uint32_t uint32_t * format
Win32Message message
FlTexture * texture
def Build(configs, env, options)
Definition: build.py:232
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however route
Definition: switches.h:137
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
#define TAG()
int32_t height
int32_t width
const uintptr_t id