5package io.flutter.view;
7import static io.flutter.Build.API_LEVELS;
9import android.annotation.SuppressLint;
10import android.annotation.TargetApi;
13import android.content.res.Configuration;
16import android.graphics.PixelFormat;
17import android.graphics.SurfaceTexture;
20import android.text.format.DateFormat;
21import android.util.AttributeSet;
23import android.view.DisplayCutout;
28import android.view.SurfaceHolder;
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;
42import androidx.annotation.NonNull;
43import androidx.annotation.RequiresApi;
44import androidx.annotation.UiThread;
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;
73import java.util.concurrent.atomic.AtomicLong;
83 implements BinaryMessenger,
85 MouseCursorPlugin.MouseCursorViewDelegate,
86 KeyboardManager.ViewDelegate {
105 private static final String TAG =
"FlutterView";
126 private final DartExecutor dartExecutor;
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;
142 private final SurfaceHolder.Callback mSurfaceCallback;
146 private final AtomicLong nextTextureId =
new AtomicLong(0L);
148 private boolean mIsSoftwareRenderingEnabled =
false;
149 private boolean didRenderFirstFrame =
false;
151 private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener =
154 public void onAccessibilityChanged(
155 boolean isAccessibilityEnabled,
boolean isTouchExplorationEnabled) {
156 resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
165 this(context, attrs,
null);
169 super(context, attrs);
171 Activity activity = ViewUtils.getActivity(getContext());
172 if (activity ==
null) {
173 throw new IllegalArgumentException(
"Bad context");
176 if (nativeView ==
null) {
179 mNativeView = nativeView;
184 mIsSoftwareRenderingEnabled = mNativeView.
getFlutterJNI().getIsSoftwareRenderingEnabled();
186 mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
187 mMetrics.physicalTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
189 setFocusableInTouchMode(
true);
194 new SurfaceHolder.Callback() {
196 public void surfaceCreated(SurfaceHolder holder) {
198 mNativeView.
getFlutterJNI().onSurfaceCreated(holder.getSurface());
202 public void surfaceChanged(SurfaceHolder holder,
int format,
int width,
int height) {
208 public void surfaceDestroyed(SurfaceHolder holder) {
213 getHolder().addCallback(mSurfaceCallback);
215 mActivityLifecycleListeners =
new ArrayList<>();
216 mFirstFrameListeners =
new ArrayList<>();
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);
228 PlatformPlugin platformPlugin =
new PlatformPlugin(activity, platformChannel);
230 new ActivityLifecycleListener() {
233 platformPlugin.updateSystemUiOverlays();
236 mImm = (InputMethodManager) getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
237 PlatformViewsController platformViewsController =
240 new TextInputPlugin(
this,
new TextInputChannel(dartExecutor), platformViewsController);
241 mKeyboardManager =
new KeyboardManager(
this);
243 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_24) {
244 mMouseCursorPlugin =
new MouseCursorPlugin(
this,
new MouseCursorChannel(dartExecutor));
246 mMouseCursorPlugin =
null;
248 mLocalizationPlugin =
new LocalizationPlugin(context, localizationChannel);
249 androidTouchProcessor =
250 new AndroidTouchProcessor(flutterRenderer,
false);
251 platformViewsController.attachToFlutterRenderer(flutterRenderer);
254 .getPlatformViewsController()
255 .attachTextInputPlugin(mTextInputPlugin);
256 mNativeView.
getFlutterJNI().setLocalizationPlugin(mLocalizationPlugin);
259 mLocalizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
260 sendUserPlatformSettingsToDart();
271 if (
event.getAction() == KeyEvent.ACTION_DOWN &&
event.getRepeatCount() == 0) {
273 getKeyDispatcherState().startTracking(
event,
this);
274 }
else if (
event.getAction() == KeyEvent.ACTION_UP) {
276 getKeyDispatcherState().handleUpEvent(
event);
282 return (isAttached() && mKeyboardManager.handleEvent(
event)) || super.dispatchKeyEvent(
event);
302 mActivityLifecycleListeners.
add(listener);
306 lifecycleChannel.appIsInactive();
310 lifecycleChannel.appIsInactive();
314 for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
315 listener.onPostResume();
317 lifecycleChannel.appIsResumed();
321 lifecycleChannel.appIsPaused();
326 systemChannel.sendMemoryPressureWarning();
334 return didRenderFirstFrame;
342 mFirstFrameListeners.
add(listener);
347 mFirstFrameListeners.remove(listener);
360 setZOrderOnTop(
false);
361 getHolder().setFormat(PixelFormat.OPAQUE);
365 navigationChannel.setInitialRoute(route);
369 navigationChannel.pushRoute(route);
373 navigationChannel.popRoute();
376 @TargetApi(API_LEVELS.API_34)
377 @RequiresApi(API_LEVELS.API_34)
379 backGestureChannel.startBackGesture(backEvent);
382 @TargetApi(API_LEVELS.API_34)
383 @RequiresApi(API_LEVELS.API_34)
385 backGestureChannel.updateBackGestureProgress(backEvent);
388 @TargetApi(API_LEVELS.API_34)
389 @RequiresApi(API_LEVELS.API_34)
391 backGestureChannel.commitBackGesture();
394 @TargetApi(API_LEVELS.API_34)
395 @RequiresApi(API_LEVELS.API_34)
397 backGestureChannel.cancelBackGesture();
400 private void sendUserPlatformSettingsToDart() {
402 boolean isNightModeOn =
403 (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
404 == Configuration.UI_MODE_NIGHT_YES;
405 SettingsChannel.PlatformBrightness brightness =
407 ? SettingsChannel.PlatformBrightness.dark
408 : SettingsChannel.PlatformBrightness.light;
412 .setTextScaleFactor(getResources().getConfiguration().fontScale)
413 .setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
414 .setPlatformBrightness(brightness)
420 super.onConfigurationChanged(newConfig);
421 mLocalizationPlugin.sendLocalesToFlutter(newConfig);
422 sendUserPlatformSettingsToDart();
430 if (!isAttached())
return null;
431 getHolder().removeCallback(mSurfaceCallback);
440 if (!isAttached())
return;
442 getHolder().removeCallback(mSurfaceCallback);
443 releaseAccessibilityNodeProvider();
451 return mTextInputPlugin.createInputConnection(
this, mKeyboardManager, outAttrs);
458 .getPlatformViewsController()
459 .checkInputConnectionProxy(view);
464 super.onProvideAutofillVirtualStructure(structure,
flags);
465 mTextInputPlugin.onProvideAutofillVirtualStructure(structure,
flags);
469 public void autofill(SparseArray<AutofillValue> values) {
470 mTextInputPlugin.autofill(values);
476 return super.onTouchEvent(
event);
479 requestUnbufferedDispatch(
event);
481 return androidTouchProcessor.onTouchEvent(
event);
487 return super.onHoverEvent(
event);
490 boolean handled = mAccessibilityNodeProvider.onAccessibilityHoverEvent(
event);
508 isAttached() && androidTouchProcessor.onGenericMotionEvent(
event, getContext());
509 return handled ?
true : super.onGenericMotionEvent(
event);
514 mMetrics.physicalWidth =
width;
515 mMetrics.physicalHeight =
height;
516 updateViewportMetrics();
517 super.onSizeChanged(
width,
height, oldWidth, oldHeight);
525 private enum ZeroSides {
532 private ZeroSides calculateShouldZeroSides() {
537 Context context = getContext();
538 int orientation = context.getResources().getConfiguration().orientation;
540 ((WindowManager) context.getSystemService(
Context.WINDOW_SERVICE))
544 if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
545 if (rotation ==
Surface.ROTATION_90) {
546 return ZeroSides.RIGHT;
547 }
else if (rotation ==
Surface.ROTATION_270) {
549 return Build.VERSION.SDK_INT >= API_LEVELS.API_23 ? ZeroSides.LEFT : ZeroSides.RIGHT;
552 else if (rotation ==
Surface.ROTATION_0 || rotation ==
Surface.ROTATION_180) {
553 return ZeroSides.BOTH;
558 return ZeroSides.NONE;
570 private int guessBottomKeyboardInset(WindowInsets insets) {
571 int screenHeight = getRootView().getHeight();
574 final double keyboardHeightRatioHeuristic = 0.18;
575 if (insets.getSystemWindowInsetBottom() < screenHeight * keyboardHeightRatioHeuristic) {
580 return insets.getSystemWindowInsetBottom();
590 @SuppressLint({
"InlinedApi",
"NewApi"})
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;
601 boolean statusBarVisible = (SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility()) == 0;
602 boolean navigationBarVisible =
603 (SYSTEM_UI_FLAG_HIDE_NAVIGATION & getWindowSystemUiVisibility()) == 0;
605 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
607 if (navigationBarVisible) {
608 mask = mask |
android.view.WindowInsets.Type.navigationBars();
610 if (statusBarVisible) {
611 mask = mask |
android.view.WindowInsets.Type.statusBars();
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;
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;
623 mMetrics.physicalViewInsetLeft = imeInsets.left;
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;
635 DisplayCutout cutout = insets.getDisplayCutout();
636 if (cutout !=
null) {
637 Insets waterfallInsets = cutout.getWaterfallInsets();
638 mMetrics.physicalViewPaddingTop =
641 cutout.getSafeInsetTop());
642 mMetrics.physicalViewPaddingRight =
645 cutout.getSafeInsetRight());
646 mMetrics.physicalViewPaddingBottom =
649 cutout.getSafeInsetBottom());
650 mMetrics.physicalViewPaddingLeft =
653 cutout.getSafeInsetLeft());
658 ZeroSides zeroSides = ZeroSides.NONE;
659 if (!navigationBarVisible) {
660 zeroSides = calculateShouldZeroSides();
665 mMetrics.physicalViewPaddingTop = statusBarVisible ? insets.getSystemWindowInsetTop() : 0;
666 mMetrics.physicalViewPaddingRight =
667 zeroSides == ZeroSides.RIGHT || zeroSides == ZeroSides.BOTH
669 : insets.getSystemWindowInsetRight();
670 mMetrics.physicalViewPaddingBottom =
671 navigationBarVisible && guessBottomKeyboardInset(insets) == 0
672 ? insets.getSystemWindowInsetBottom()
674 mMetrics.physicalViewPaddingLeft =
675 zeroSides == ZeroSides.LEFT || zeroSides == ZeroSides.BOTH
677 : insets.getSystemWindowInsetLeft();
680 mMetrics.physicalViewInsetTop = 0;
681 mMetrics.physicalViewInsetRight = 0;
682 mMetrics.physicalViewInsetBottom = guessBottomKeyboardInset(insets);
683 mMetrics.physicalViewInsetLeft = 0;
686 updateViewportMetrics();
687 return super.onApplyWindowInsets(insets);
690 private boolean isAttached() {
691 return mNativeView !=
null && mNativeView.
isAttached();
695 if (!isAttached())
throw new AssertionError(
"Platform view is not attached");
698 private void preRun() {
703 if (mAccessibilityNodeProvider !=
null) {
704 mAccessibilityNodeProvider.reset();
708 private void postRun() {}
727 private void updateViewportMetrics() {
728 if (!isAttached())
return;
755 didRenderFirstFrame =
true;
760 listener.onFirstFrame();
766 super.onAttachedToWindow();
768 PlatformViewsController platformViewsController =
770 mAccessibilityNodeProvider =
774 (AccessibilityManager) getContext().getSystemService(
Context.ACCESSIBILITY_SERVICE),
775 getContext().getContentResolver(),
776 platformViewsController);
777 mAccessibilityNodeProvider.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
780 mAccessibilityNodeProvider.isAccessibilityEnabled(),
781 mAccessibilityNodeProvider.isTouchExplorationEnabled());
786 super.onDetachedFromWindow();
787 releaseAccessibilityNodeProvider();
792 private void resetWillNotDraw(
boolean isAccessibilityEnabled,
boolean isTouchExplorationEnabled) {
793 if (!mIsSoftwareRenderingEnabled) {
794 setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled));
796 setWillNotDraw(
false);
802 if (mAccessibilityNodeProvider !=
null && mAccessibilityNodeProvider.isAccessibilityEnabled()) {
803 return mAccessibilityNodeProvider;
812 private void releaseAccessibilityNodeProvider() {
813 if (mAccessibilityNodeProvider !=
null) {
814 mAccessibilityNodeProvider.release();
815 mAccessibilityNodeProvider =
null;
822 @TargetApi(API_LEVELS.API_24)
823 @RequiresApi(API_LEVELS.API_24)
826 return PointerIcon.getSystemIcon(getContext(),
type);
840 return mTextInputPlugin.handleKeyEvent(keyEvent);
845 getRootView().dispatchKeyEvent(keyEvent);
866 Log.
d(
TAG,
"FlutterView.send called on a detached view, channel=" + channel);
881 @NonNull String channel,
882 @NonNull BinaryMessageHandler handler,
883 @NonNull TaskQueue taskQueue) {
895 final SurfaceTexture surfaceTexture =
new SurfaceTexture(0);
902 throw new UnsupportedOperationException(
"Image textures are not supported in this mode.");
907 throw new UnsupportedOperationException(
908 "SurfaceProducer textures are not supported in this mode.");
914 @NonNull SurfaceTexture surfaceTexture) {
915 surfaceTexture.detachFromGLContext();
918 mNativeView.
getFlutterJNI().registerTexture(entry.id(), entry.textureWrapper());
923 private final long id;
924 private final SurfaceTextureWrapper textureWrapper;
925 private boolean released;
936 this.
surfaceTexture().setOnFrameAvailableListener(onFrameListener,
new Handler());
939 private SurfaceTexture.OnFrameAvailableListener onFrameListener =
940 new SurfaceTexture.OnFrameAvailableListener() {
942 public void onFrameAvailable(SurfaceTexture
texture) {
943 if (released || mNativeView ==
null) {
953 .markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id);
958 return textureWrapper;
964 return textureWrapper.surfaceTexture();
987 textureWrapper.release();
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
static void d(@NonNull String tag, @NonNull String message)
static void e(@NonNull String tag, @NonNull String message)
static String getLookupKeyForAsset(@NonNull String asset)
void setMessageHandler(String channel, BinaryMessageHandler handler)
FlutterJNI getFlutterJNI()
void send(String channel, ByteBuffer message)
void detachFromFlutterView()
FlutterPluginRegistry getPluginRegistry()
void attachViewAndActivity(FlutterView flutterView, Activity activity)
DartExecutor getDartExecutor()
void runFromBundle(FlutterRunArguments args)
SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture)
SurfaceTexture surfaceTexture()
SurfaceTextureWrapper textureWrapper()
int physicalViewInsetBottom
int physicalViewPaddingBottom
int physicalViewPaddingLeft
int systemGestureInsetTop
int systemGestureInsetRight
int physicalViewPaddingRight
int physicalViewInsetLeft
int systemGestureInsetBottom
int physicalViewInsetRight
int systemGestureInsetLeft
int physicalViewPaddingTop
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 resetAccessibilityTree()
float getDevicePixelRatio()
void onConfigurationChanged(Configuration newConfig)
void redispatch(@NonNull KeyEvent keyEvent)
final WindowInsets onApplyWindowInsets(WindowInsets insets)
void disableBufferingIncomingMessages()
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)
void onDetachedFromWindow()
TextureRegistry.SurfaceTextureEntry registerSurfaceTexture( @NonNull SurfaceTexture surfaceTexture)
void enableBufferingIncomingMessages()
void disableTransparentBackground()
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 hasRenderedFirstFrame()
boolean onTextInputKeyEvent(@NonNull KeyEvent keyEvent)
AccessibilityNodeProvider getAccessibilityNodeProvider()
FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView)
void onAttachedToWindow()
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
uint32_t uint32_t * format
FlutterView getFlutterView()