2018-11-07 12:24:35 -08:00
// Copyright 2013 The Flutter Authors. All rights reserved.
2015-01-31 00:44:42 -08:00
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
2016-04-08 09:39:14 -07:00
package io.flutter.view ;
2015-01-31 00:44:42 -08:00
2019-03-08 13:33:58 -08:00
import android.annotation.TargetApi ;
2019-08-16 13:42:56 -04:00
import android.annotation.SuppressLint ;
2016-05-02 14:24:40 -07:00
import android.app.Activity ;
2015-01-31 00:44:42 -08:00
import android.content.Context ;
2019-04-09 03:30:36 +10:00
import android.content.ContextWrapper ;
2016-03-14 16:37:58 -07:00
import android.content.res.Configuration ;
2016-09-16 14:00:49 -07:00
import android.graphics.Bitmap ;
2019-08-16 13:42:56 -04:00
import android.graphics.Insets ;
2018-11-07 09:59:51 -08:00
import android.graphics.PixelFormat ;
2016-01-27 13:20:05 -08:00
import android.graphics.Rect ;
2017-11-02 02:57:29 -07:00
import android.graphics.SurfaceTexture ;
2015-07-15 11:07:53 -07:00
import android.os.Build ;
2018-11-07 09:59:51 -08:00
import android.os.Handler ;
2019-02-20 17:05:31 -08:00
import android.os.LocaleList ;
2019-06-24 11:17:18 -07:00
import android.support.annotation.NonNull ;
2019-03-08 13:33:58 -08:00
import android.support.annotation.RequiresApi ;
2019-05-13 13:26:31 -07:00
import android.support.annotation.UiThread ;
2017-10-18 16:13:43 -07:00
import android.text.format.DateFormat ;
2016-03-11 14:43:35 -08:00
import android.util.AttributeSet ;
2016-01-21 11:22:09 -08:00
import android.util.Log ;
2019-07-19 16:46:02 -07:00
import android.view.KeyEvent ;
import android.view.MotionEvent ;
import android.view.Surface ;
import android.view.SurfaceHolder ;
import android.view.SurfaceView ;
import android.view.View ;
import android.view.WindowInsets ;
2016-01-27 13:20:05 -08:00
import android.view.accessibility.AccessibilityManager ;
import android.view.accessibility.AccessibilityNodeProvider ;
2015-03-09 16:19:56 -07:00
import android.view.inputmethod.EditorInfo ;
import android.view.inputmethod.InputConnection ;
2018-05-24 23:21:33 +01:00
import android.view.inputmethod.InputMethodManager ;
2019-07-19 16:46:02 -07:00
import java.nio.ByteBuffer ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.Locale ;
import java.util.concurrent.atomic.AtomicLong ;
2017-11-30 11:05:55 -08:00
import io.flutter.app.FlutterPluginRegistry ;
2019-03-20 11:30:49 -07:00
import io.flutter.embedding.android.AndroidKeyProcessor ;
import io.flutter.embedding.android.AndroidTouchProcessor ;
2019-06-24 11:17:18 -07:00
import io.flutter.embedding.engine.FlutterJNI ;
2019-02-04 19:30:15 -08:00
import io.flutter.embedding.engine.dart.DartExecutor ;
2019-03-11 16:04:44 -07:00
import io.flutter.embedding.engine.renderer.FlutterRenderer ;
2019-02-20 17:05:31 -08:00
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel ;
2019-02-06 17:07:30 -08:00
import io.flutter.embedding.engine.systemchannels.KeyEventChannel ;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel ;
2019-02-20 17:05:31 -08:00
import io.flutter.embedding.engine.systemchannels.LocalizationChannel ;
2019-02-06 17:07:30 -08:00
import io.flutter.embedding.engine.systemchannels.NavigationChannel ;
2019-02-20 17:05:31 -08:00
import io.flutter.embedding.engine.systemchannels.PlatformChannel ;
2019-02-04 19:30:15 -08:00
import io.flutter.embedding.engine.systemchannels.SettingsChannel ;
2019-02-06 17:07:30 -08:00
import io.flutter.embedding.engine.systemchannels.SystemChannel ;
2019-07-19 16:46:02 -07:00
import io.flutter.plugin.common.ActivityLifecycleListener ;
import io.flutter.plugin.common.BinaryMessenger ;
2017-03-17 09:04:59 +01:00
import io.flutter.plugin.editing.TextInputPlugin ;
import io.flutter.plugin.platform.PlatformPlugin ;
2019-03-19 15:48:45 -07:00
import io.flutter.plugin.platform.PlatformViewsController ;
2015-01-31 00:44:42 -08:00
/**
2016-04-08 09:39:14 -07:00
* An Android view containing a Flutter app.
2015-01-31 00:44:42 -08:00
*/
2019-03-08 18:09:04 -08:00
public class FlutterView extends SurfaceView implements BinaryMessenger , TextureRegistry {
2017-06-07 12:28:41 -07:00
/**
* Interface for those objects that maintain and expose a reference to a
* {@code FlutterView} (such as a full-screen Flutter activity).
2017-06-09 07:35:50 -07:00
*
2018-07-31 18:18:19 -07:00
* <p>
* This indirection is provided to support applications that use an activity
* other than {@link io.flutter.app.FlutterActivity} (e.g. Android v4 support
* library's {@code FragmentActivity}). It allows Flutter plugins to deal in
* this interface and not require that the activity be a subclass of
* {@code FlutterActivity}.
* </p>
2017-06-07 12:28:41 -07:00
*/
public interface Provider {
/**
2018-07-31 18:18:19 -07:00
* Returns a reference to the Flutter view maintained by this object. This may
* be {@code null}.
2017-06-07 12:28:41 -07:00
*/
FlutterView getFlutterView ();
}
2016-04-08 09:39:14 -07:00
private static final String TAG = "FlutterView" ;
2015-02-27 16:46:19 -08:00
2017-03-17 09:04:59 +01:00
static final class ViewportMetrics {
2016-10-27 13:12:55 -07:00
float devicePixelRatio = 1 . 0f ;
int physicalWidth = 0 ;
int physicalHeight = 0 ;
int physicalPaddingTop = 0 ;
int physicalPaddingRight = 0 ;
int physicalPaddingBottom = 0 ;
int physicalPaddingLeft = 0 ;
2017-11-29 16:27:15 -08:00
int physicalViewInsetTop = 0 ;
int physicalViewInsetRight = 0 ;
int physicalViewInsetBottom = 0 ;
int physicalViewInsetLeft = 0 ;
2019-08-16 13:42:56 -04:00
int systemGestureInsetTop = 0 ;
int systemGestureInsetRight = 0 ;
int systemGestureInsetBottom = 0 ;
int systemGestureInsetLeft = 0 ;
2016-10-27 13:12:55 -07:00
}
2019-02-04 19:30:15 -08:00
private final DartExecutor dartExecutor ;
2019-03-11 16:04:44 -07:00
private final FlutterRenderer flutterRenderer ;
2019-02-06 17:07:30 -08:00
private final NavigationChannel navigationChannel ;
private final KeyEventChannel keyEventChannel ;
private final LifecycleChannel lifecycleChannel ;
2019-02-20 17:05:31 -08:00
private final LocalizationChannel localizationChannel ;
private final PlatformChannel platformChannel ;
2019-02-06 17:07:30 -08:00
private final SettingsChannel settingsChannel ;
private final SystemChannel systemChannel ;
2018-05-24 23:21:33 +01:00
private final InputMethodManager mImm ;
2017-03-17 09:04:59 +01:00
private final TextInputPlugin mTextInputPlugin ;
2019-02-11 15:27:55 -08:00
private final AndroidKeyProcessor androidKeyProcessor ;
2019-03-11 16:04:44 -07:00
private final AndroidTouchProcessor androidTouchProcessor ;
2019-02-20 17:05:31 -08:00
private AccessibilityBridge mAccessibilityNodeProvider ;
2015-01-31 00:44:42 -08:00
private final SurfaceHolder . Callback mSurfaceCallback ;
2016-01-21 11:22:09 -08:00
private final ViewportMetrics mMetrics ;
2017-03-17 09:04:59 +01:00
private final List < ActivityLifecycleListener > mActivityLifecycleListeners ;
2017-08-14 15:44:52 -07:00
private final List < FirstFrameListener > mFirstFrameListeners ;
2017-11-02 02:57:29 -07:00
private final AtomicLong nextTextureId = new AtomicLong ( 0L );
2017-11-09 12:10:00 -08:00
private FlutterNativeView mNativeView ;
2017-05-31 17:27:47 -07:00
private boolean mIsSoftwareRenderingEnabled = false ; // using the software renderer or not
2019-07-19 16:46:02 -07:00
private boolean didRenderFirstFrame = false ;
2017-05-31 17:27:47 -07:00
2019-03-08 18:09:04 -08:00
private final AccessibilityBridge . OnAccessibilityChangeListener onAccessibilityChangeListener = new AccessibilityBridge . OnAccessibilityChangeListener () {
@Override
public void onAccessibilityChanged ( boolean isAccessibilityEnabled , boolean isTouchExplorationEnabled ) {
resetWillNotDraw ( isAccessibilityEnabled , isTouchExplorationEnabled );
}
};
2016-04-08 09:39:14 -07:00
public FlutterView ( Context context ) {
2016-03-11 14:43:35 -08:00
this ( context , null );
}
2016-04-08 09:39:14 -07:00
public FlutterView ( Context context , AttributeSet attrs ) {
2017-11-13 13:56:48 -08:00
this ( context , attrs , null );
}
public FlutterView ( Context context , AttributeSet attrs , FlutterNativeView nativeView ) {
2016-03-11 14:43:35 -08:00
super ( context , attrs );
2015-01-31 00:44:42 -08:00
2019-04-09 03:30:36 +10:00
Activity activity = getActivity ( getContext ());
if ( activity == null ) {
throw new IllegalArgumentException ( "Bad context" );
}
2017-11-13 13:56:48 -08:00
if ( nativeView == null ) {
2017-11-30 11:05:55 -08:00
mNativeView = new FlutterNativeView ( activity . getApplicationContext ());
2017-11-13 13:56:48 -08:00
} else {
mNativeView = nativeView ;
}
2019-02-20 17:05:31 -08:00
dartExecutor = mNativeView . getDartExecutor ();
2019-03-11 16:04:44 -07:00
flutterRenderer = new FlutterRenderer ( mNativeView . getFlutterJNI ());
2019-10-14 14:03:25 -07:00
mIsSoftwareRenderingEnabled = mNativeView . getFlutterJNI (). nativeGetIsSoftwareRenderingEnabled ();
2018-12-21 01:07:04 -05:00
mMetrics = new ViewportMetrics ();
mMetrics . devicePixelRatio = context . getResources (). getDisplayMetrics (). density ;
setFocusable ( true );
setFocusableInTouchMode ( true );
2017-11-30 11:05:55 -08:00
mNativeView . attachViewAndActivity ( this , activity );
2015-01-31 00:44:42 -08:00
2016-09-16 14:46:36 -07:00
mSurfaceCallback = new SurfaceHolder . Callback () {
2015-01-31 00:44:42 -08:00
@Override
public void surfaceCreated ( SurfaceHolder holder ) {
2017-08-17 10:24:12 -07:00
assertAttached ();
2018-12-21 01:07:04 -05:00
mNativeView . getFlutterJNI (). onSurfaceCreated ( holder . getSurface ());
2015-01-31 00:44:42 -08:00
}
2016-09-16 14:46:36 -07:00
@Override
public void surfaceChanged ( SurfaceHolder holder , int format , int width , int height ) {
2017-08-17 10:24:12 -07:00
assertAttached ();
2018-12-21 01:07:04 -05:00
mNativeView . getFlutterJNI (). onSurfaceChanged ( width , height );
2016-09-16 14:46:36 -07:00
}
2015-01-31 00:44:42 -08:00
@Override
public void surfaceDestroyed ( SurfaceHolder holder ) {
2017-08-17 10:24:12 -07:00
assertAttached ();
2018-12-21 01:07:04 -05:00
mNativeView . getFlutterJNI (). onSurfaceDestroyed ();
2015-01-31 00:44:42 -08:00
}
};
getHolder (). addCallback ( mSurfaceCallback );
2015-02-27 16:46:19 -08:00
2017-03-17 09:04:59 +01:00
mActivityLifecycleListeners = new ArrayList <> ();
2017-08-14 15:44:52 -07:00
mFirstFrameListeners = new ArrayList <> ();
2017-03-17 09:04:59 +01:00
2019-02-20 17:05:31 -08:00
// Create all platform channels
2019-02-06 17:07:30 -08:00
navigationChannel = new NavigationChannel ( dartExecutor );
keyEventChannel = new KeyEventChannel ( dartExecutor );
lifecycleChannel = new LifecycleChannel ( dartExecutor );
2019-02-20 17:05:31 -08:00
localizationChannel = new LocalizationChannel ( dartExecutor );
platformChannel = new PlatformChannel ( dartExecutor );
2019-02-06 17:07:30 -08:00
systemChannel = new SystemChannel ( dartExecutor );
2019-02-04 19:30:15 -08:00
settingsChannel = new SettingsChannel ( dartExecutor );
2017-10-17 13:44:09 -07:00
2019-02-20 17:05:31 -08:00
// Create and setup plugins
PlatformPlugin platformPlugin = new PlatformPlugin ( activity , platformChannel );
2019-07-29 17:40:25 -07:00
addActivityLifecycleListener ( new ActivityLifecycleListener () {
@Override
public void onPostResume () {
platformPlugin . updateSystemUiOverlays ();
}
});
2018-05-24 23:21:33 +01:00
mImm = ( InputMethodManager ) getContext (). getSystemService ( Context . INPUT_METHOD_SERVICE );
2019-06-10 12:56:35 -07:00
PlatformViewsController platformViewsController = mNativeView . getPluginRegistry (). getPlatformViewsController ();
mTextInputPlugin = new TextInputPlugin ( this , dartExecutor , platformViewsController );
2019-02-25 19:15:49 -08:00
androidKeyProcessor = new AndroidKeyProcessor ( keyEventChannel , mTextInputPlugin );
2019-03-11 16:04:44 -07:00
androidTouchProcessor = new AndroidTouchProcessor ( flutterRenderer );
2019-06-10 12:56:35 -07:00
mNativeView . getPluginRegistry (). getPlatformViewsController (). attachTextInputPlugin ( mTextInputPlugin );
2018-10-17 10:53:01 -07:00
2019-02-20 17:05:31 -08:00
// Send initial platform information to Dart
sendLocalesToDart ( getResources (). getConfiguration ());
2019-02-04 19:30:15 -08:00
sendUserPlatformSettingsToDart ();
2015-11-10 16:16:49 -08:00
}
2019-06-03 11:04:33 -07:00
2019-04-09 03:30:36 +10:00
private static Activity getActivity ( Context context ) {
if ( context == null ) {
return null ;
}
if ( context instanceof Activity ) {
return ( Activity ) context ;
}
if ( context instanceof ContextWrapper ) {
// Recurse up chain of base contexts until we find an Activity.
return getActivity ((( ContextWrapper ) context ). getBaseContext ());
}
return null ;
}
2015-11-10 16:16:49 -08:00
2019-06-03 11:04:33 -07:00
@NonNull
public DartExecutor getDartExecutor () {
return dartExecutor ;
}
2015-11-10 16:16:49 -08:00
@Override
public boolean onKeyUp ( int keyCode , KeyEvent event ) {
2017-03-17 09:04:59 +01:00
if ( ! isAttached ()) {
2016-12-12 16:30:26 -08:00
return super . onKeyUp ( keyCode , event );
2016-10-17 16:55:42 -07:00
}
2019-02-11 15:27:55 -08:00
androidKeyProcessor . onKeyUp ( event );
2016-10-19 16:46:55 -07:00
return super . onKeyUp ( keyCode , event );
2015-11-10 16:16:49 -08:00
}
@Override
public boolean onKeyDown ( int keyCode , KeyEvent event ) {
2017-03-17 09:04:59 +01:00
if ( ! isAttached ()) {
2016-12-12 16:30:26 -08:00
return super . onKeyDown ( keyCode , event );
2016-10-17 16:55:42 -07:00
}
2019-02-11 15:27:55 -08:00
androidKeyProcessor . onKeyDown ( event );
2016-10-19 16:46:55 -07:00
return super . onKeyDown ( keyCode , event );
2015-01-31 00:44:42 -08:00
}
2017-11-13 13:56:48 -08:00
public FlutterNativeView getFlutterNativeView () {
return mNativeView ;
}
2017-11-30 11:05:55 -08:00
public FlutterPluginRegistry getPluginRegistry () {
return mNativeView . getPluginRegistry ();
}
2018-03-16 12:59:57 +01:00
public String getLookupKeyForAsset ( String asset ) {
return FlutterMain . getLookupKeyForAsset ( asset );
}
public String getLookupKeyForAsset ( String asset , String packageName ) {
return FlutterMain . getLookupKeyForAsset ( asset , packageName );
}
2016-07-12 10:12:32 -07:00
public void addActivityLifecycleListener ( ActivityLifecycleListener listener ) {
mActivityLifecycleListeners . add ( listener );
}
2018-05-04 14:46:57 -07:00
public void onStart () {
2019-02-06 17:07:30 -08:00
lifecycleChannel . appIsInactive ();
2018-05-04 14:46:57 -07:00
}
2016-03-14 16:37:58 -07:00
public void onPause () {
2019-02-06 17:07:30 -08:00
lifecycleChannel . appIsInactive ();
2016-03-14 16:37:58 -07:00
}
2016-05-02 14:24:40 -07:00
public void onPostResume () {
2017-03-17 09:04:59 +01:00
for ( ActivityLifecycleListener listener : mActivityLifecycleListeners ) {
2016-07-12 10:12:32 -07:00
listener . onPostResume ();
2017-03-17 09:04:59 +01:00
}
2019-02-06 17:07:30 -08:00
lifecycleChannel . appIsResumed ();
2016-03-14 16:37:58 -07:00
}
2017-05-24 16:34:34 -07:00
public void onStop () {
2019-02-06 17:07:30 -08:00
lifecycleChannel . appIsPaused ();
2017-05-24 16:34:34 -07:00
}
2017-03-14 15:42:30 -07:00
public void onMemoryPressure () {
2019-02-06 17:07:30 -08:00
systemChannel . sendMemoryPressureWarning ();
2017-03-14 15:42:30 -07:00
}
2019-07-19 16:46:02 -07:00
/**
* Returns true if the Flutter experience associated with this {@code FlutterView} has
* rendered its first frame, or false otherwise.
*/
public boolean hasRenderedFirstFrame () {
return didRenderFirstFrame ;
}
2017-08-14 15:44:52 -07:00
/**
2018-07-31 18:18:19 -07:00
* Provide a listener that will be called once when the FlutterView renders its
* first frame to the underlaying SurfaceView.
2017-08-14 15:44:52 -07:00
*/
public void addFirstFrameListener ( FirstFrameListener listener ) {
mFirstFrameListeners . add ( listener );
}
/**
* Remove an existing first frame listener.
*/
public void removeFirstFrameListener ( FirstFrameListener listener ) {
mFirstFrameListeners . remove ( listener );
}
2018-09-05 16:47:02 -07:00
/**
* Updates this to support rendering as a transparent {@link SurfaceView}.
*
* Sets it on top of its window. The background color still needs to be
* controlled from within the Flutter UI itself.
2019-07-29 12:46:26 -07:00
*
* @deprecated This breaks accessibility highlighting. See https://github.com/flutter/flutter/issues/37025.
2018-09-05 16:47:02 -07:00
*/
2019-07-29 12:46:26 -07:00
@Deprecated
2018-09-05 16:47:02 -07:00
public void enableTransparentBackground () {
2019-07-29 12:46:26 -07:00
Log . w ( TAG , "Warning: FlutterView is set on top of the window. Accessibility highlights will not be visible in the Flutter UI. https://github.com/flutter/flutter/issues/37025" );
2018-09-05 16:47:02 -07:00
setZOrderOnTop ( true );
getHolder (). setFormat ( PixelFormat . TRANSPARENT );
}
/**
* Reverts this back to the {@link SurfaceView} defaults, at the back of its
* window and opaque.
*/
public void disableTransparentBackground () {
setZOrderOnTop ( false );
getHolder (). setFormat ( PixelFormat . OPAQUE );
}
2017-06-06 10:59:41 +02:00
public void setInitialRoute ( String route ) {
2019-02-06 17:07:30 -08:00
navigationChannel . setInitialRoute ( route );
2017-06-06 10:59:41 +02:00
}
2016-03-14 16:37:58 -07:00
public void pushRoute ( String route ) {
2019-02-06 17:07:30 -08:00
navigationChannel . pushRoute ( route );
2016-03-14 16:37:58 -07:00
}
public void popRoute () {
2019-02-06 17:07:30 -08:00
navigationChannel . popRoute ();
2016-03-14 16:37:58 -07:00
}
2019-02-04 19:30:15 -08:00
private void sendUserPlatformSettingsToDart () {
// Lookup the current brightness of the Android OS.
boolean isNightModeOn = ( getResources (). getConfiguration (). uiMode & Configuration . UI_MODE_NIGHT_MASK ) == Configuration . UI_MODE_NIGHT_YES ;
SettingsChannel . PlatformBrightness brightness = isNightModeOn
? SettingsChannel . PlatformBrightness . dark
: SettingsChannel . PlatformBrightness . light ;
settingsChannel
. startMessage ()
. setTextScaleFactor ( getResources (). getConfiguration (). fontScale )
. setUse24HourFormat ( DateFormat . is24HourFormat ( getContext ()))
. setPlatformBrightness ( brightness )
. send ();
2017-09-29 13:19:06 -07:00
}
2017-10-17 13:44:09 -07:00
2019-02-26 17:03:10 -08:00
@SuppressWarnings ( "deprecation" )
2019-02-20 17:05:31 -08:00
private void sendLocalesToDart ( Configuration config ) {
List < Locale > locales = new ArrayList <> ();
2019-02-25 10:29:16 +04:00
if ( Build . VERSION . SDK_INT >= android . os . Build . VERSION_CODES . N ) {
LocaleList localeList = config . getLocales ();
int localeCount = localeList . size ();
for ( int index = 0 ; index < localeCount ; ++ index ) {
Locale locale = localeList . get ( index );
locales . add ( locale );
}
} else {
locales . add ( config . locale );
2018-10-17 10:53:01 -07:00
}
2019-02-20 17:05:31 -08:00
localizationChannel . sendLocales ( locales );
2016-03-14 16:37:58 -07:00
}
@Override
protected void onConfigurationChanged ( Configuration newConfig ) {
super . onConfigurationChanged ( newConfig );
2019-02-20 17:05:31 -08:00
sendLocalesToDart ( newConfig );
2019-02-04 19:30:15 -08:00
sendUserPlatformSettingsToDart ();
2016-03-14 16:37:58 -07:00
}
2016-01-27 13:20:05 -08:00
float getDevicePixelRatio () {
return mMetrics . devicePixelRatio ;
}
2017-11-13 13:56:48 -08:00
public FlutterNativeView detach () {
2018-07-31 18:18:19 -07:00
if ( ! isAttached ())
return null ;
2017-11-13 13:56:48 -08:00
getHolder (). removeCallback ( mSurfaceCallback );
2019-03-21 18:08:58 -07:00
mNativeView . detachFromFlutterView ();
2017-11-13 13:56:48 -08:00
FlutterNativeView view = mNativeView ;
mNativeView = null ;
return view ;
}
2016-03-11 14:43:35 -08:00
public void destroy () {
2018-07-31 18:18:19 -07:00
if ( ! isAttached ())
return ;
2017-08-17 10:24:12 -07:00
2015-01-31 00:44:42 -08:00
getHolder (). removeCallback ( mSurfaceCallback );
2017-11-13 13:16:48 -08:00
mNativeView . destroy ();
mNativeView = null ;
2015-01-31 00:44:42 -08:00
}
2015-03-09 16:19:56 -07:00
@Override
public InputConnection onCreateInputConnection ( EditorInfo outAttrs ) {
2019-02-20 17:05:31 -08:00
return mTextInputPlugin . createInputConnection ( this , outAttrs );
2015-03-09 16:19:56 -07:00
}
2019-06-10 12:56:35 -07:00
@Override
public boolean checkInputConnectionProxy ( View view ) {
2019-06-21 14:30:58 -07:00
return mNativeView . getPluginRegistry (). getPlatformViewsController (). checkInputConnectionProxy ( view );
2019-06-10 12:56:35 -07:00
}
2015-02-26 12:23:18 -08:00
@Override
public boolean onTouchEvent ( MotionEvent event ) {
2017-03-17 09:04:59 +01:00
if ( ! isAttached ()) {
2019-03-11 16:04:44 -07:00
return super . onTouchEvent ( event );
2017-03-17 09:04:59 +01:00
}
2016-12-12 16:30:26 -08:00
2015-07-15 11:07:53 -07:00
// TODO(abarth): This version check might not be effective in some
// versions of Android that statically compile code and will be upset
// at the lack of |requestUnbufferedDispatch|. Instead, we should factor
// version-dependent code into separate classes for each supported
// version and dispatch dynamically.
if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . LOLLIPOP ) {
requestUnbufferedDispatch ( event );
}
2015-02-27 16:46:19 -08:00
2019-03-11 16:04:44 -07:00
return androidTouchProcessor . onTouchEvent ( event );
2015-02-18 16:57:32 -08:00
}
2016-02-04 16:56:07 -08:00
@Override
public boolean onHoverEvent ( MotionEvent event ) {
2017-03-17 09:04:59 +01:00
if ( ! isAttached ()) {
2019-03-11 16:04:44 -07:00
return super . onHoverEvent ( event );
2017-03-17 09:04:59 +01:00
}
2016-12-12 16:30:26 -08:00
2019-03-08 18:09:04 -08:00
boolean handled = mAccessibilityNodeProvider . onAccessibilityHoverEvent ( event );
2016-02-04 16:56:07 -08:00
if ( ! handled ) {
// TODO(ianh): Expose hover events to the platform,
// implementing ADD, REMOVE, etc.
}
return handled ;
}
2019-03-11 16:04:44 -07:00
/**
* Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover,
* track pad touches, scroll wheel movements, etc.
*
* Flutter handles all of its own gesture detection and processing, therefore this
* method forwards all {@link MotionEvent} data from Android to Flutter.
*/
2019-01-14 16:15:20 -08:00
@Override
public boolean onGenericMotionEvent ( MotionEvent event ) {
2019-03-11 16:04:44 -07:00
boolean handled = isAttached () && androidTouchProcessor . onGenericMotionEvent ( event );
return handled ? true : super . onGenericMotionEvent ( event );
2019-01-14 16:15:20 -08:00
}
2016-01-21 11:22:09 -08:00
@Override
protected void onSizeChanged ( int width , int height , int oldWidth , int oldHeight ) {
mMetrics . physicalWidth = width ;
mMetrics . physicalHeight = height ;
2016-10-27 13:12:55 -07:00
updateViewportMetrics ();
2016-01-21 11:22:09 -08:00
super . onSizeChanged ( width , height , oldWidth , oldHeight );
}
2018-09-19 18:09:10 -07:00
// TODO(garyq): Add support for notch cutout API
// Decide if we want to zero the padding of the sides. When in Landscape orientation,
// android may decide to place the software navigation bars on the side. When the nav
// bar is hidden, the reported insets should be removed to prevent extra useless space
// on the sides.
enum ZeroSides { NONE , LEFT , RIGHT , BOTH }
ZeroSides calculateShouldZeroSides () {
// We get both orientation and rotation because rotation is all 4
// rotations relative to default rotation while orientation is portrait
// or landscape. By combining both, we can obtain a more precise measure
// of the rotation.
Activity activity = ( Activity ) getContext ();
int orientation = activity . getResources (). getConfiguration (). orientation ;
int rotation = activity . getWindowManager (). getDefaultDisplay (). getRotation ();
if ( orientation == Configuration . ORIENTATION_LANDSCAPE ) {
if ( rotation == Surface . ROTATION_90 ) {
return ZeroSides . RIGHT ;
}
else if ( rotation == Surface . ROTATION_270 ) {
// In android API >= 23, the nav bar always appears on the "bottom" (USB) side.
return Build . VERSION . SDK_INT >= 23 ? ZeroSides . LEFT : ZeroSides . RIGHT ;
}
// Ambiguous orientation due to landscape left/right default. Zero both sides.
else if ( rotation == Surface . ROTATION_0 || rotation == Surface . ROTATION_180 ) {
return ZeroSides . BOTH ;
}
}
// Square orientation deprecated in API 16, we will not check for it and return false
// to be safe and not remove any unique padding for the devices that do use it.
return ZeroSides . NONE ;
}
2018-09-24 11:38:13 -07:00
// TODO(garyq): Use clean ways to detect keyboard instead of heuristics if possible
// TODO(garyq): The keyboard detection may interact strangely with
// https://github.com/flutter/flutter/issues/22061
// Uses inset heights and screen heights as a heuristic to determine if the insets should
// be padded. When the on-screen keyboard is detected, we want to include the full inset
// but when the inset is just the hidden nav bar, we want to provide a zero inset so the space
// can be used.
2019-03-08 13:33:58 -08:00
@TargetApi ( 20 )
@RequiresApi ( 20 )
2018-09-24 11:38:13 -07:00
int calculateBottomKeyboardInset ( WindowInsets insets ) {
int screenHeight = getRootView (). getHeight ();
// Magic number due to this being a heuristic. This should be replaced, but we have not
// found a clean way to do it yet (Sept. 2018)
final double keyboardHeightRatioHeuristic = 0 . 18 ;
if ( insets . getSystemWindowInsetBottom () < screenHeight * keyboardHeightRatioHeuristic ) {
// Is not a keyboard, so return zero as inset.
return 0 ;
}
else {
// Is a keyboard, so return the full inset.
return insets . getSystemWindowInsetBottom ();
}
}
2018-09-19 18:09:10 -07:00
// This callback is not present in API < 20, which means lower API devices will see
// the wider than expected padding when the status and navigation bars are hidden.
2019-08-16 13:42:56 -04:00
// The annotations to suppress "InlinedApi" and "NewApi" lints prevent lint warnings
// caused by usage of Android Q APIs. These calls are safe because they are
// guarded.
2016-01-21 11:22:09 -08:00
@Override
2019-03-08 13:33:58 -08:00
@TargetApi ( 20 )
@RequiresApi ( 20 )
2019-08-16 13:42:56 -04:00
@SuppressLint ({ "InlinedApi" , "NewApi" })
2016-01-21 11:22:09 -08:00
public final WindowInsets onApplyWindowInsets ( WindowInsets insets ) {
2018-09-19 18:09:10 -07:00
boolean statusBarHidden =
( SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility ()) != 0 ;
boolean navigationBarHidden =
( SYSTEM_UI_FLAG_HIDE_NAVIGATION & getWindowSystemUiVisibility ()) != 0 ;
// We zero the left and/or right sides to prevent the padding the
// navigation bar would have caused.
ZeroSides zeroSides = ZeroSides . NONE ;
if ( navigationBarHidden ) {
zeroSides = calculateShouldZeroSides ();
}
// The padding on top should be removed when the statusbar is hidden.
mMetrics . physicalPaddingTop = statusBarHidden ? 0 : insets . getSystemWindowInsetTop ();
mMetrics . physicalPaddingRight =
zeroSides == ZeroSides . RIGHT || zeroSides == ZeroSides . BOTH ? 0 : insets . getSystemWindowInsetRight ();
2017-12-12 11:04:42 -08:00
mMetrics . physicalPaddingBottom = 0 ;
2018-09-19 18:09:10 -07:00
mMetrics . physicalPaddingLeft =
zeroSides == ZeroSides . LEFT || zeroSides == ZeroSides . BOTH ? 0 : insets . getSystemWindowInsetLeft ();
2017-12-12 11:04:42 -08:00
// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
mMetrics . physicalViewInsetTop = 0 ;
mMetrics . physicalViewInsetRight = 0 ;
2018-09-24 11:38:13 -07:00
// We perform hidden navbar and keyboard handling if the navbar is set to hidden. Otherwise,
// the navbar padding should always be provided.
mMetrics . physicalViewInsetBottom =
navigationBarHidden ? calculateBottomKeyboardInset ( insets ) : insets . getSystemWindowInsetBottom ();
2017-12-12 11:04:42 -08:00
mMetrics . physicalViewInsetLeft = 0 ;
2019-08-16 13:42:56 -04:00
if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . Q ) {
Insets systemGestureInsets = insets . getSystemGestureInsets ();
mMetrics . systemGestureInsetTop = systemGestureInsets . top ;
mMetrics . systemGestureInsetRight = systemGestureInsets . right ;
mMetrics . systemGestureInsetBottom = systemGestureInsets . bottom ;
mMetrics . systemGestureInsetLeft = systemGestureInsets . left ;
}
2016-10-27 13:12:55 -07:00
updateViewportMetrics ();
2016-01-21 11:22:09 -08:00
return super . onApplyWindowInsets ( insets );
}
2017-01-17 15:36:47 -08:00
@Override
@SuppressWarnings ( "deprecation" )
protected boolean fitSystemWindows ( Rect insets ) {
if ( Build . VERSION . SDK_INT <= Build . VERSION_CODES . KITKAT ) {
2017-12-12 11:04:42 -08:00
// Status bar, left/right system insets partially obscure content (padding).
2017-01-17 15:36:47 -08:00
mMetrics . physicalPaddingTop = insets . top ;
mMetrics . physicalPaddingRight = insets . right ;
2017-12-12 11:04:42 -08:00
mMetrics . physicalPaddingBottom = 0 ;
2017-01-17 15:36:47 -08:00
mMetrics . physicalPaddingLeft = insets . left ;
2017-12-12 11:04:42 -08:00
// Bottom system inset (keyboard) should adjust scrollable bottom edge (inset).
mMetrics . physicalViewInsetTop = 0 ;
mMetrics . physicalViewInsetRight = 0 ;
2017-11-29 16:27:15 -08:00
mMetrics . physicalViewInsetBottom = insets . bottom ;
2017-12-12 11:04:42 -08:00
mMetrics . physicalViewInsetLeft = 0 ;
2017-01-17 15:36:47 -08:00
updateViewportMetrics ();
return true ;
} else {
return super . fitSystemWindows ( insets );
}
}
2016-12-12 16:30:26 -08:00
private boolean isAttached () {
2017-12-11 17:15:21 -08:00
return mNativeView != null && mNativeView . isAttached ();
2016-12-12 16:30:26 -08:00
}
2017-08-17 10:24:12 -07:00
void assertAttached () {
2018-07-31 18:18:19 -07:00
if ( ! isAttached ())
throw new AssertionError ( "Platform view is not attached" );
2017-08-17 10:24:12 -07:00
}
2016-07-20 10:35:45 -07:00
private void preRun () {
2016-01-27 13:20:05 -08:00
resetAccessibilityTree ();
2016-07-20 10:35:45 -07:00
}
2019-03-08 18:09:04 -08:00
void resetAccessibilityTree () {
if ( mAccessibilityNodeProvider != null ) {
mAccessibilityNodeProvider . reset ();
}
}
2018-07-31 18:18:19 -07:00
private void postRun () {
}
2016-07-20 10:35:45 -07:00
2018-08-07 12:42:22 -07:00
public void runFromBundle ( FlutterRunArguments args ) {
assertAttached ();
preRun ();
mNativeView . runFromBundle ( args );
postRun ();
}
2016-07-20 10:35:45 -07:00
2017-02-03 16:25:25 -08:00
/**
* Return the most recent frame as a bitmap.
2017-03-17 09:04:59 +01:00
*
2017-02-03 16:25:25 -08:00
* @return A bitmap.
*/
2016-09-16 14:00:49 -07:00
public Bitmap getBitmap () {
2017-08-17 10:24:12 -07:00
assertAttached ();
2018-12-21 01:07:04 -05:00
return mNativeView . getFlutterJNI (). getBitmap ();
2016-09-16 14:00:49 -07:00
}
2016-10-27 13:12:55 -07:00
private void updateViewportMetrics () {
2018-07-31 18:18:19 -07:00
if ( ! isAttached ())
return ;
2019-08-16 13:42:56 -04:00
mNativeView . getFlutterJNI (). setViewportMetrics (
mMetrics . devicePixelRatio ,
mMetrics . physicalWidth ,
mMetrics . physicalHeight ,
mMetrics . physicalPaddingTop ,
mMetrics . physicalPaddingRight ,
mMetrics . physicalPaddingBottom ,
mMetrics . physicalPaddingLeft ,
mMetrics . physicalViewInsetTop ,
mMetrics . physicalViewInsetRight ,
mMetrics . physicalViewInsetBottom ,
mMetrics . physicalViewInsetLeft ,
mMetrics . systemGestureInsetTop ,
mMetrics . systemGestureInsetRight ,
mMetrics . systemGestureInsetBottom ,
mMetrics . systemGestureInsetLeft
);
2016-10-27 13:12:55 -07:00
}
2019-07-19 16:46:02 -07:00
// Called by FlutterNativeView to notify first Flutter frame rendered.
2017-11-09 12:10:00 -08:00
public void onFirstFrame () {
2019-07-19 16:46:02 -07:00
didRenderFirstFrame = true ;
2018-03-26 15:43:38 -07:00
// Allow listeners to remove themselves when they are called.
List < FirstFrameListener > listeners = new ArrayList <> ( mFirstFrameListeners );
for ( FirstFrameListener listener : listeners ) {
2017-08-14 15:44:52 -07:00
listener . onFirstFrame ();
}
}
2016-01-27 13:20:05 -08:00
@Override
protected void onAttachedToWindow () {
super . onAttachedToWindow ();
2018-07-31 18:18:19 -07:00
2019-03-19 15:48:45 -07:00
PlatformViewsController platformViewsController = getPluginRegistry (). getPlatformViewsController ();
2019-03-08 18:09:04 -08:00
mAccessibilityNodeProvider = new AccessibilityBridge (
this ,
2019-03-14 14:24:49 -07:00
new AccessibilityChannel ( dartExecutor , getFlutterNativeView (). getFlutterJNI ()),
2019-03-08 18:09:04 -08:00
( AccessibilityManager ) getContext (). getSystemService ( Context . ACCESSIBILITY_SERVICE ),
2019-03-19 15:48:45 -07:00
getContext (). getContentResolver (),
platformViewsController
2019-03-08 18:09:04 -08:00
);
mAccessibilityNodeProvider . setOnAccessibilityChangeListener ( onAccessibilityChangeListener );
2016-01-27 13:20:05 -08:00
2019-03-08 18:09:04 -08:00
resetWillNotDraw (
mAccessibilityNodeProvider . isAccessibilityEnabled (),
mAccessibilityNodeProvider . isTouchExplorationEnabled ()
);
2018-07-31 18:18:19 -07:00
}
2016-02-08 10:11:24 -08:00
@Override
protected void onDetachedFromWindow () {
super . onDetachedFromWindow ();
2019-03-08 18:09:04 -08:00
mAccessibilityNodeProvider . release ();
mAccessibilityNodeProvider = null ;
2016-02-08 10:11:24 -08:00
}
2019-03-08 18:09:04 -08:00
// TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments.
private void resetWillNotDraw ( boolean isAccessibilityEnabled , boolean isTouchExplorationEnabled ) {
2017-05-31 17:27:47 -07:00
if ( ! mIsSoftwareRenderingEnabled ) {
2019-03-08 18:09:04 -08:00
setWillNotDraw ( ! ( isAccessibilityEnabled || isTouchExplorationEnabled ));
2017-05-31 17:27:47 -07:00
} else {
setWillNotDraw ( false );
}
2016-02-08 10:11:24 -08:00
}
2016-01-27 13:20:05 -08:00
@Override
public AccessibilityNodeProvider getAccessibilityNodeProvider () {
2019-03-11 23:45:03 -07:00
if ( mAccessibilityNodeProvider != null && mAccessibilityNodeProvider . isAccessibilityEnabled ()) {
2018-07-31 18:18:19 -07:00
return mAccessibilityNodeProvider ;
2016-02-04 16:56:07 -08:00
} else {
2019-03-08 18:09:04 -08:00
// TODO(goderbauer): when a11y is off this should return a one-off snapshot of
// the a11y
// tree.
return null ;
2016-02-04 16:56:07 -08:00
}
}
2016-01-27 13:20:05 -08:00
2017-04-18 14:30:31 +02:00
@Override
2019-05-13 13:26:31 -07:00
@UiThread
2017-04-18 14:30:31 +02:00
public void send ( String channel , ByteBuffer message ) {
2017-12-11 17:15:21 -08:00
send ( channel , message , null );
2017-04-18 14:30:31 +02:00
}
@Override
2019-05-13 13:26:31 -07:00
@UiThread
2017-04-18 14:30:31 +02:00
public void send ( String channel , ByteBuffer message , BinaryReply callback ) {
2017-12-11 17:15:21 -08:00
if ( ! isAttached ()) {
Log . d ( TAG , "FlutterView.send called on a detached view, channel=" + channel );
return ;
}
2017-11-09 12:10:00 -08:00
mNativeView . send ( channel , message , callback );
2017-03-01 13:54:32 +01:00
}
2017-04-18 14:30:31 +02:00
@Override
2019-05-13 13:26:31 -07:00
@UiThread
2017-04-18 14:30:31 +02:00
public void setMessageHandler ( String channel , BinaryMessageHandler handler ) {
2017-11-09 12:10:00 -08:00
mNativeView . setMessageHandler ( channel , handler );
2016-03-16 16:41:58 -07:00
}
2017-08-14 15:44:52 -07:00
/**
2018-07-31 18:18:19 -07:00
* Listener will be called on the Android UI thread once when Flutter renders
* the first frame.
2017-08-14 15:44:52 -07:00
*/
2018-07-31 18:18:19 -07:00
public interface FirstFrameListener {
void onFirstFrame ();
}
2017-11-02 02:57:29 -07:00
@Override
public TextureRegistry . SurfaceTextureEntry createSurfaceTexture () {
final SurfaceTexture surfaceTexture = new SurfaceTexture ( 0 );
surfaceTexture . detachFromGLContext ();
2018-07-31 18:18:19 -07:00
final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry ( nextTextureId . getAndIncrement (),
surfaceTexture );
2018-12-21 01:07:04 -05:00
mNativeView . getFlutterJNI (). registerTexture ( entry . id (), surfaceTexture );
2017-11-02 02:57:29 -07:00
return entry ;
}
final class SurfaceTextureRegistryEntry implements TextureRegistry . SurfaceTextureEntry {
private final long id ;
private final SurfaceTexture surfaceTexture ;
private boolean released ;
SurfaceTextureRegistryEntry ( long id , SurfaceTexture surfaceTexture ) {
this . id = id ;
this . surfaceTexture = surfaceTexture ;
2018-10-10 15:01:48 -07:00
if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . LOLLIPOP ) {
// The callback relies on being executed on the UI thread (unsynchronised read of mNativeView
// and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable),
// so we explicitly pass a Handler for the current thread.
this . surfaceTexture . setOnFrameAvailableListener ( onFrameListener , new Handler ());
} else {
// Android documentation states that the listener can be called on an arbitrary thread.
// But in practice, versions of Android that predate the newer API will call the listener
// on the thread where the SurfaceTexture was constructed.
this . surfaceTexture . setOnFrameAvailableListener ( onFrameListener );
}
2017-11-02 02:57:29 -07:00
}
2018-10-10 15:01:48 -07:00
private SurfaceTexture . OnFrameAvailableListener onFrameListener = new SurfaceTexture . OnFrameAvailableListener () {
@Override
public void onFrameAvailable ( SurfaceTexture texture ) {
2019-02-16 06:01:41 +09:00
if ( released || mNativeView == null ) {
2018-10-10 15:01:48 -07:00
// Even though we make sure to unregister the callback before releasing, as of Android O
// SurfaceTexture has a data race when accessing the callback, so the callback may
// still be called by a stale reference after released==true and mNativeView==null.
return ;
}
2018-12-21 01:07:04 -05:00
mNativeView . getFlutterJNI (). markTextureFrameAvailable ( SurfaceTextureRegistryEntry . this . id );
2018-10-10 15:01:48 -07:00
}
};
2017-11-02 02:57:29 -07:00
@Override
public SurfaceTexture surfaceTexture () {
2018-07-16 09:04:20 -07:00
return surfaceTexture ;
2017-11-02 02:57:29 -07:00
}
@Override
public long id () {
return id ;
}
@Override
public void release () {
if ( released ) {
return ;
}
released = true ;
2019-02-15 13:11:49 -08:00
// The ordering of the next 3 calls is important:
// First we remove the frame listener, then we release the SurfaceTexture, and only after we unregister
// the texture which actually deletes the GL texture.
2018-09-04 15:31:01 +02:00
// Otherwise onFrameAvailableListener might be called after mNativeView==null
// (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable.
surfaceTexture . setOnFrameAvailableListener ( null );
2017-11-02 02:57:29 -07:00
surfaceTexture . release ();
2019-02-15 13:11:49 -08:00
mNativeView . getFlutterJNI (). unregisterTexture ( id );
2017-11-02 02:57:29 -07:00
}
}
2015-01-31 00:44:42 -08:00
}