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
}