From 2595b0aaaf3cfc114acd6846ee0851830d9556ee Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Sat, 1 Sep 2018 00:16:07 -0700 Subject: [PATCH 01/11] Copied over FlutterView. Copied a number of dependencies due to package change. Grouped related methods within FlutterView. Deleted a few unused methods. --- .../io/flutter/embedding/FlutterFragment.java | 6 +- .../io/flutter/embedding/FlutterView.java | 1896 ++++++++--------- .../embedding/legacy/AccessibilityBridge.java | 34 +- .../embedding/legacy/FlutterNativeView.java | 7 +- .../legacy/FlutterPluginRegistry.java | 9 +- .../legacy/InputConnectionAdaptor.java | 8 +- .../embedding/legacy/PlatformViewFactory.java | 1 - .../legacy/PlatformViewRegistry.java | 1 - .../legacy/PlatformViewsController.java | 14 +- .../embedding/legacy/PluginRegistry.java | 1 - .../legacy/SingleViewPresentation.java | 9 +- .../embedding/legacy/TextInputPlugin.java | 17 +- .../legacy/VirtualDisplayController.java | 9 +- 13 files changed, 983 insertions(+), 1029 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index 7f7360f2135d1..fc2391a1ba2c6 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -26,10 +26,8 @@ import android.view.WindowManager; import android.widget.FrameLayout; import io.flutter.app.BuildConfig; -import io.flutter.app.FlutterActivity; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.view.FlutterNativeView; -import io.flutter.view.FlutterView; +import io.flutter.embedding.legacy.PluginRegistry; +import io.flutter.embedding.legacy.FlutterNativeView; /** * {@code Fragment} which displays a {@link FlutterView} that takes up all available space. diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index fb2b789250786..21af5c1e296ba 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -4,6 +4,8 @@ package io.flutter.embedding; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; @@ -23,1030 +25,996 @@ import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.WindowInsets; -import android.view.WindowManager; +import android.view.*; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; - +import io.flutter.embedding.legacy.AccessibilityBridge; +import io.flutter.embedding.legacy.FlutterNativeView; +import io.flutter.embedding.legacy.FlutterPluginRegistry; +import io.flutter.embedding.legacy.TextInputPlugin; +import io.flutter.plugin.common.*; +import io.flutter.plugin.platform.PlatformPlugin; +import io.flutter.view.FlutterRunArguments; +import io.flutter.view.TextureRegistry; +import io.flutter.view.VsyncWaiter; import org.json.JSONException; import org.json.JSONObject; import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.concurrent.atomic.AtomicLong; -import io.flutter.app.FlutterPluginRegistry; -import io.flutter.plugin.common.ActivityLifecycleListener; -import io.flutter.plugin.common.BasicMessageChannel; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.JSONMessageCodec; -import io.flutter.plugin.common.JSONMethodCodec; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.StandardMessageCodec; -import io.flutter.plugin.common.StringCodec; -import io.flutter.plugin.editing.TextInputPlugin; -import io.flutter.plugin.platform.PlatformPlugin; -import io.flutter.embedding.legacy.AccessibilityBridge; -import io.flutter.view.FlutterMain; -import io.flutter.embedding.legacy.FlutterNativeView; -import io.flutter.view.FlutterRunArguments; -import io.flutter.view.TextureRegistry; -import io.flutter.view.VsyncWaiter; - /** * An Android view containing a Flutter app. */ -public class FlutterView extends SurfaceView - implements BinaryMessenger, TextureRegistry, AccessibilityManager.AccessibilityStateChangeListener { - /** - * Interface for those objects that maintain and expose a reference to a - * {@code FlutterView} (such as a full-screen Flutter activity). - * - *

- * 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}. - *

- */ - public interface Provider { - /** - * Returns a reference to the Flutter view maintained by this object. This may - * be {@code null}. - */ - FlutterView getFlutterView(); - } - - private static final String TAG = "FlutterView"; - - private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER"; - - static final class ViewportMetrics { - float devicePixelRatio = 1.0f; - int physicalWidth = 0; - int physicalHeight = 0; - int physicalPaddingTop = 0; - int physicalPaddingRight = 0; - int physicalPaddingBottom = 0; - int physicalPaddingLeft = 0; - int physicalViewInsetTop = 0; - int physicalViewInsetRight = 0; - int physicalViewInsetBottom = 0; - int physicalViewInsetLeft = 0; - } - - private final InputMethodManager mImm; - private final TextInputPlugin mTextInputPlugin; - private final SurfaceHolder.Callback mSurfaceCallback; - private final ViewportMetrics mMetrics; - private final AccessibilityManager mAccessibilityManager; - private final MethodChannel mFlutterLocalizationChannel; - private final MethodChannel mFlutterNavigationChannel; - private final BasicMessageChannel mFlutterKeyEventChannel; - private final BasicMessageChannel mFlutterLifecycleChannel; - private final BasicMessageChannel mFlutterSystemChannel; - private final BasicMessageChannel mFlutterSettingsChannel; - private final BroadcastReceiver mDiscoveryReceiver; - private final List mActivityLifecycleListeners; - private final List mFirstFrameListeners; - private final AtomicLong nextTextureId = new AtomicLong(0L); - private FlutterNativeView mNativeView; - private final AnimationScaleObserver mAnimationScaleObserver; - private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not - private InputConnection mLastInputConnection; - - public FlutterView(Context context) { - this(context, null); - } - - public FlutterView(Context context, AttributeSet attrs) { - this(context, attrs, null); - } - - public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { - super(context, attrs); - - mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled(); - mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); - mMetrics = new ViewportMetrics(); - mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; - setFocusable(true); - setFocusableInTouchMode(true); - - Activity activity = (Activity) getContext(); - if (nativeView == null) { - mNativeView = new FlutterNativeView(activity.getApplicationContext()); - } else { - mNativeView = nativeView; - } - mNativeView.attachViewAndActivity(this, activity); - - int color = 0xFF000000; - TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true); - if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) { - color = typedValue.data; - } - // TODO(abarth): Consider letting the developer override this color. - final int backgroundColor = color; - - mSurfaceCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceCreated(SurfaceHolder holder) { - assertAttached(); - nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - assertAttached(); - nativeSurfaceChanged(mNativeView.get(), width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - assertAttached(); - nativeSurfaceDestroyed(mNativeView.get()); - } - }; - getHolder().addCallback(mSurfaceCallback); - - mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - - mActivityLifecycleListeners = new ArrayList<>(); - mFirstFrameListeners = new ArrayList<>(); - - // Configure the platform plugins and flutter channels. - mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE); - mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE); - mFlutterKeyEventChannel = new BasicMessageChannel<>(this, "flutter/keyevent", JSONMessageCodec.INSTANCE); - mFlutterLifecycleChannel = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE); - mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system", JSONMessageCodec.INSTANCE); - mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings", JSONMessageCodec.INSTANCE); - - PlatformPlugin platformPlugin = new PlatformPlugin(activity); - MethodChannel flutterPlatformChannel = new MethodChannel(this, "flutter/platform", JSONMethodCodec.INSTANCE); - flutterPlatformChannel.setMethodCallHandler(platformPlugin); - addActivityLifecycleListener(platformPlugin); - mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - mTextInputPlugin = new TextInputPlugin(this); - - setLocale(getResources().getConfiguration().locale); - setUserSettings(); - - if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { - mDiscoveryReceiver = new DiscoveryReceiver(); - context.registerReceiver(mDiscoveryReceiver, new IntentFilter(ACTION_DISCOVER)); - } else { - mDiscoveryReceiver = null; - } - } - - private void encodeKeyEvent(KeyEvent event, Map message) { - message.put("flags", event.getFlags()); - message.put("codePoint", event.getUnicodeChar()); - message.put("keyCode", event.getKeyCode()); - message.put("scanCode", event.getScanCode()); - message.put("metaState", event.getMetaState()); - } - +@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) +public class FlutterView extends SurfaceView implements + BinaryMessenger, + TextureRegistry, + AccessibilityManager.AccessibilityStateChangeListener { + + private static final String TAG = "FlutterView"; + + private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER"; + + // Must match the PointerChange enum in pointer.dart. + private static final int kPointerChangeCancel = 0; + private static final int kPointerChangeAdd = 1; + private static final int kPointerChangeRemove = 2; + private static final int kPointerChangeHover = 3; + private static final int kPointerChangeDown = 4; + private static final int kPointerChangeMove = 5; + private static final int kPointerChangeUp = 6; + + // Must match the PointerDeviceKind enum in pointer.dart. + private static final int kPointerDeviceKindTouch = 0; + private static final int kPointerDeviceKindMouse = 1; + private static final int kPointerDeviceKindStylus = 2; + private static final int kPointerDeviceKindInvertedStylus = 3; + private static final int kPointerDeviceKindUnknown = 4; + + static final class ViewportMetrics { + float devicePixelRatio = 1.0f; + int physicalWidth = 0; + int physicalHeight = 0; + int physicalPaddingTop = 0; + int physicalPaddingRight = 0; + int physicalPaddingBottom = 0; + int physicalPaddingLeft = 0; + int physicalViewInsetTop = 0; + int physicalViewInsetRight = 0; + int physicalViewInsetBottom = 0; + int physicalViewInsetLeft = 0; + } + + private final InputMethodManager mImm; + private final TextInputPlugin mTextInputPlugin; + private final SurfaceHolder.Callback mSurfaceCallback; + private final ViewportMetrics mMetrics; + private final AccessibilityManager mAccessibilityManager; + private final MethodChannel mFlutterLocalizationChannel; + private final MethodChannel mFlutterNavigationChannel; + private final BasicMessageChannel mFlutterKeyEventChannel; + private final BasicMessageChannel mFlutterLifecycleChannel; + private final BasicMessageChannel mFlutterSystemChannel; + private final BasicMessageChannel mFlutterSettingsChannel; + private final BroadcastReceiver mDiscoveryReceiver; + private final List mActivityLifecycleListeners; + private final List mFirstFrameListeners; + private final AtomicLong nextTextureId = new AtomicLong(0L); + private FlutterNativeView mNativeView; + private final AnimationScaleObserver mAnimationScaleObserver; + private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not + private InputConnection mLastInputConnection; + + // Accessibility + private boolean mAccessibilityEnabled = false; + private boolean mTouchExplorationEnabled = false; + private int mAccessibilityFeatureFlags = 0; + private TouchExplorationListener mTouchExplorationListener; + + //------ START VIEW OVERRIDES ----- + public FlutterView(Context context) { + this(context, null); + } + + public FlutterView(Context context, AttributeSet attrs) { + this(context, attrs, null); + } + + public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { + super(context, attrs); + + mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled(); + mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); + mMetrics = new ViewportMetrics(); + mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + setFocusable(true); + setFocusableInTouchMode(true); + + Activity activity = (Activity) getContext(); + if (nativeView == null) { + mNativeView = new FlutterNativeView(activity.getApplicationContext()); + } else { + mNativeView = nativeView; + } + mNativeView.attachViewAndActivity(this, activity); + + int color = 0xFF000000; + TypedValue typedValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true); + if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) { + color = typedValue.data; + } + // TODO(abarth): Consider letting the developer override this color. + final int backgroundColor = color; + + mSurfaceCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + assertFlutterEngineAttached(); + nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + assertFlutterEngineAttached(); + nativeSurfaceChanged(mNativeView.get(), width, height); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + assertFlutterEngineAttached(); + nativeSurfaceDestroyed(mNativeView.get()); + } + }; + getHolder().addCallback(mSurfaceCallback); + + mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + + mActivityLifecycleListeners = new ArrayList<>(); + mFirstFrameListeners = new ArrayList<>(); + + // Configure the platform plugins and flutter channels. + mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE); + mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE); + mFlutterKeyEventChannel = new BasicMessageChannel<>(this, "flutter/keyevent", JSONMessageCodec.INSTANCE); + mFlutterLifecycleChannel = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE); + mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system", JSONMessageCodec.INSTANCE); + mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings", JSONMessageCodec.INSTANCE); + + PlatformPlugin platformPlugin = new PlatformPlugin(activity); + MethodChannel flutterPlatformChannel = new MethodChannel(this, "flutter/platform", JSONMethodCodec.INSTANCE); + flutterPlatformChannel.setMethodCallHandler(platformPlugin); + addActivityLifecycleListener(platformPlugin); + mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + mTextInputPlugin = new TextInputPlugin(this); + + setLocale(getResources().getConfiguration().locale); + setUserSettings(); + + if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + mDiscoveryReceiver = new DiscoveryReceiver(); + context.registerReceiver(mDiscoveryReceiver, new IntentFilter(ACTION_DISCOVER)); + } else { + mDiscoveryReceiver = null; + } + } + + /** + * Broadcast receiver used to discover active Flutter instances. + * + * This is used by the `flutter` tool to find the observatory ports for all the + * active Flutter views. We dump the data to the logs and the tool scrapes the + * log lines for the data. + */ + private class DiscoveryReceiver extends BroadcastReceiver { @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (!isAttached()) { - return super.onKeyUp(keyCode, event); + public void onReceive(Context context, Intent intent) { + URI observatoryUri = URI.create(FlutterNativeView.getObservatoryUri()); + JSONObject discover = new JSONObject(); + try { + discover.put("id", getContext().getPackageName()); + discover.put("observatoryPort", observatoryUri.getPort()); + Log.i(TAG, "DISCOVER: " + discover); // The tool looks for this data. See android_device.dart. + } catch (JSONException e) { + } + } + } + + public void addActivityLifecycleListener(ActivityLifecycleListener listener) { + mActivityLifecycleListeners.add(listener); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mAccessibilityEnabled = mAccessibilityManager.isEnabled(); + mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); + getContext().getContentResolver().registerContentObserver(transitionUri, false, mAnimationScaleObserver); + } + + if (mAccessibilityEnabled || mTouchExplorationEnabled) { + ensureAccessibilityEnabled(); + } + if (mTouchExplorationEnabled) { + mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; + } + // Apply additional accessibility settings + updateAccessibilityFeatures(); + resetWillNotDraw(); + mAccessibilityManager.addAccessibilityStateChangeListener(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (mTouchExplorationListener == null) { + mTouchExplorationListener = new TouchExplorationListener(); + } + mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getContext().getContentResolver().unregisterContentObserver(mAnimationScaleObserver); + mAccessibilityManager.removeAccessibilityStateChangeListener(this); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener); + } + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + try { + mLastInputConnection = mTextInputPlugin.createInputConnection(this, outAttrs); + return mLastInputConnection; + } catch (JSONException e) { + Log.e(TAG, "Failed to create input connection", e); + return null; + } + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + setLocale(newConfig.locale); + setUserSettings(); + } + + private void setLocale(Locale locale) { + mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry())); + } + + private void setUserSettings() { + Map message = new HashMap<>(); + message.put("textScaleFactor", getResources().getConfiguration().fontScale); + message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext())); + mFlutterSettingsChannel.send(message); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isFlutterEngineAttached()) { + return false; + } + + // 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); + } + + // These values must match the unpacking code in hooks.dart. + final int kPointerDataFieldCount = 19; + final int kBytePerField = 8; + + int pointerCount = event.getPointerCount(); + + ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField); + packet.order(ByteOrder.LITTLE_ENDIAN); + + int maskedAction = event.getActionMasked(); + // ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN + // only apply to a single pointer, other events apply to all pointers. + if (maskedAction == MotionEvent.ACTION_UP || maskedAction == MotionEvent.ACTION_POINTER_UP + || maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN) { + addPointerForIndex(event, event.getActionIndex(), packet); + } else { + // ACTION_MOVE may not actually mean all pointers have moved + // but it's the responsibility of a later part of the system to + // ignore 0-deltas if desired. + for (int p = 0; p < pointerCount; p++) { + addPointerForIndex(event, p, packet); + } + } + + assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; + nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + return true; + } + + private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer packet) { + int pointerChange = getPointerChangeForAction(event.getActionMasked()); + if (pointerChange == -1) { + return; + } + + int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex)); + + long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. + + packet.putLong(timeStamp); // time_stamp + packet.putLong(pointerChange); // change + packet.putLong(pointerKind); // kind + packet.putLong(event.getPointerId(pointerIndex)); // device + packet.putDouble(event.getX(pointerIndex)); // physical_x + packet.putDouble(event.getY(pointerIndex)); // physical_y + + if (pointerKind == kPointerDeviceKindMouse) { + packet.putLong(event.getButtonState() & 0x1F); // buttons + } else if (pointerKind == kPointerDeviceKindStylus) { + packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons + } else { + packet.putLong(0); // buttons + } + + packet.putLong(0); // obscured + + // TODO(eseidel): Could get the calibrated range if necessary: + // event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE) + packet.putDouble(event.getPressure(pointerIndex)); // pressure + packet.putDouble(0.0); // pressure_min + packet.putDouble(1.0); // pressure_max + + if (pointerKind == kPointerDeviceKindStylus) { + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance + packet.putDouble(0.0); // distance_max + } else { + packet.putDouble(0.0); // distance + packet.putDouble(0.0); // distance_max + } + + packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major + packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor + + packet.putDouble(0.0); // radius_min + packet.putDouble(0.0); // radius_max + + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation + + if (pointerKind == kPointerDeviceKindStylus) { + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt + } else { + packet.putDouble(0.0); // tilt + } + } + + private int getPointerChangeForAction(int maskedAction) { + // Primary pointer: + if (maskedAction == MotionEvent.ACTION_DOWN) { + return kPointerChangeDown; + } + if (maskedAction == MotionEvent.ACTION_UP) { + return kPointerChangeUp; + } + // Secondary pointer: + if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) { + return kPointerChangeDown; + } + if (maskedAction == MotionEvent.ACTION_POINTER_UP) { + return kPointerChangeUp; + } + // All pointers: + if (maskedAction == MotionEvent.ACTION_MOVE) { + return kPointerChangeMove; + } + if (maskedAction == MotionEvent.ACTION_CANCEL) { + return kPointerChangeCancel; + } + return -1; + } + + private int getPointerDeviceTypeForToolType(int toolType) { + switch (toolType) { + case MotionEvent.TOOL_TYPE_FINGER: + return kPointerDeviceKindTouch; + case MotionEvent.TOOL_TYPE_STYLUS: + return kPointerDeviceKindStylus; + case MotionEvent.TOOL_TYPE_MOUSE: + return kPointerDeviceKindMouse; + case MotionEvent.TOOL_TYPE_ERASER: + return kPointerDeviceKindInvertedStylus; + default: + // MotionEvent.TOOL_TYPE_UNKNOWN will reach here. + return kPointerDeviceKindUnknown; + } + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + if (!isFlutterEngineAttached()) { + return false; + } + + boolean handled = handleAccessibilityHoverEvent(event); + if (!handled) { + // TODO(ianh): Expose hover events to the platform, + // implementing ADD, REMOVE, etc. + } + return handled; + } + + @Override + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { + mMetrics.physicalWidth = width; + mMetrics.physicalHeight = height; + updateViewportMetrics(); + super.onSizeChanged(width, height, oldWidth, oldHeight); + } + + // TODO(mattcarroll): window insets are API 20. what should we do for lower APIs? + @SuppressLint("NewApi") + @Override + public final WindowInsets onApplyWindowInsets(WindowInsets insets) { + // Status bar, left/right system insets partially obscure content (padding). + mMetrics.physicalPaddingTop = insets.getSystemWindowInsetTop(); + mMetrics.physicalPaddingRight = insets.getSystemWindowInsetRight(); + mMetrics.physicalPaddingBottom = 0; + mMetrics.physicalPaddingLeft = insets.getSystemWindowInsetLeft(); + + // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). + mMetrics.physicalViewInsetTop = 0; + mMetrics.physicalViewInsetRight = 0; + mMetrics.physicalViewInsetBottom = insets.getSystemWindowInsetBottom(); + mMetrics.physicalViewInsetLeft = 0; + updateViewportMetrics(); + return super.onApplyWindowInsets(insets); + } + + @Override + @SuppressWarnings("deprecation") + protected boolean fitSystemWindows(Rect insets) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + // Status bar, left/right system insets partially obscure content (padding). + mMetrics.physicalPaddingTop = insets.top; + mMetrics.physicalPaddingRight = insets.right; + mMetrics.physicalPaddingBottom = 0; + mMetrics.physicalPaddingLeft = insets.left; + + // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). + mMetrics.physicalViewInsetTop = 0; + mMetrics.physicalViewInsetRight = 0; + mMetrics.physicalViewInsetBottom = insets.bottom; + mMetrics.physicalViewInsetLeft = 0; + updateViewportMetrics(); + return true; + } else { + return super.fitSystemWindows(insets); + } + } + + private void updateViewportMetrics() { + if (!isFlutterEngineAttached()) + return; + nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, + mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, + mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, + mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); + + WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + float fps = wm.getDefaultDisplay().getRefreshRate(); + VsyncWaiter.refreshPeriodNanos = (long) (1000000000.0 / fps); + } + + // TODO(mattcarroll): detachFromGLContext requires API 16. what should we do for earlier APIs? + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public SurfaceTextureEntry createSurfaceTexture() { + final SurfaceTexture surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), + surfaceTexture); + nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); + 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; + this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture texture) { + nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); } - - Map message = new HashMap<>(); - message.put("type", "keyup"); - message.put("keymap", "android"); - encodeKeyEvent(event, message); - mFlutterKeyEventChannel.send(message); - return super.onKeyUp(keyCode, event); + }); } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (!isAttached()) { - return super.onKeyDown(keyCode, event); - } - - if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { - if (mLastInputConnection != null && mImm.isAcceptingText()) { - mLastInputConnection.sendKeyEvent(event); - } - } - - Map message = new HashMap<>(); - message.put("type", "keydown"); - message.put("keymap", "android"); - encodeKeyEvent(event, message); - mFlutterKeyEventChannel.send(message); - return super.onKeyDown(keyCode, event); - } - - public FlutterNativeView getFlutterNativeView() { - return mNativeView; - } - - public FlutterPluginRegistry getPluginRegistry() { - return mNativeView.getPluginRegistry(); - } - - public String getLookupKeyForAsset(String asset) { - return FlutterMain.getLookupKeyForAsset(asset); - } - - public String getLookupKeyForAsset(String asset, String packageName) { - return FlutterMain.getLookupKeyForAsset(asset, packageName); - } - - public void addActivityLifecycleListener(ActivityLifecycleListener listener) { - mActivityLifecycleListeners.add(listener); - } - - public void onStart() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); - } - - public void onPause() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); - } - - public void onPostResume() { - updateAccessibilityFeatures(); - for (ActivityLifecycleListener listener : mActivityLifecycleListeners) { - listener.onPostResume(); - } - mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); - } - - public void onStop() { - mFlutterLifecycleChannel.send("AppLifecycleState.paused"); - } - - public void onMemoryPressure() { - Map message = new HashMap<>(1); - message.put("type", "memoryPressure"); - mFlutterSystemChannel.send(message); - } - - /** - * Provide a listener that will be called once when the FlutterView renders its - * first frame to the underlaying SurfaceView. - */ - public void addFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.add(listener); - } - - /** - * Remove an existing first frame listener. - */ - public void removeFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.remove(listener); - } - - public void setInitialRoute(String route) { - mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); - } - - public void pushRoute(String route) { - mFlutterNavigationChannel.invokeMethod("pushRoute", route); - } - - public void popRoute() { - mFlutterNavigationChannel.invokeMethod("popRoute", null); - } - - private void setUserSettings() { - Map message = new HashMap<>(); - message.put("textScaleFactor", getResources().getConfiguration().fontScale); - message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext())); - mFlutterSettingsChannel.send(message); - } - - private void setLocale(Locale locale) { - mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry())); + public SurfaceTexture surfaceTexture() { + return surfaceTexture; } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - setLocale(newConfig.locale); - setUserSettings(); - } - - float getDevicePixelRatio() { - return mMetrics.devicePixelRatio; - } - - public FlutterNativeView detach() { - if (!isAttached()) - return null; - if (mDiscoveryReceiver != null) { - getContext().unregisterReceiver(mDiscoveryReceiver); - } - getHolder().removeCallback(mSurfaceCallback); - mNativeView.detach(); - - FlutterNativeView view = mNativeView; - mNativeView = null; - return view; - } - - public void destroy() { - if (!isAttached()) - return; - - if (mDiscoveryReceiver != null) { - getContext().unregisterReceiver(mDiscoveryReceiver); - } - - getHolder().removeCallback(mSurfaceCallback); - - mNativeView.destroy(); - mNativeView = null; + public long id() { + return id; } @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - try { - mLastInputConnection = mTextInputPlugin.createInputConnection(this, outAttrs); - return mLastInputConnection; - } catch (JSONException e) { - Log.e(TAG, "Failed to create input connection", e); - return null; - } - } - - // Must match the PointerChange enum in pointer.dart. - private static final int kPointerChangeCancel = 0; - private static final int kPointerChangeAdd = 1; - private static final int kPointerChangeRemove = 2; - private static final int kPointerChangeHover = 3; - private static final int kPointerChangeDown = 4; - private static final int kPointerChangeMove = 5; - private static final int kPointerChangeUp = 6; - - // Must match the PointerDeviceKind enum in pointer.dart. - private static final int kPointerDeviceKindTouch = 0; - private static final int kPointerDeviceKindMouse = 1; - private static final int kPointerDeviceKindStylus = 2; - private static final int kPointerDeviceKindInvertedStylus = 3; - private static final int kPointerDeviceKindUnknown = 4; - - private int getPointerChangeForAction(int maskedAction) { - // Primary pointer: - if (maskedAction == MotionEvent.ACTION_DOWN) { - return kPointerChangeDown; - } - if (maskedAction == MotionEvent.ACTION_UP) { - return kPointerChangeUp; - } - // Secondary pointer: - if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) { - return kPointerChangeDown; - } - if (maskedAction == MotionEvent.ACTION_POINTER_UP) { - return kPointerChangeUp; - } - // All pointers: - if (maskedAction == MotionEvent.ACTION_MOVE) { - return kPointerChangeMove; - } - if (maskedAction == MotionEvent.ACTION_CANCEL) { - return kPointerChangeCancel; - } - return -1; - } - - private int getPointerDeviceTypeForToolType(int toolType) { - switch (toolType) { - case MotionEvent.TOOL_TYPE_FINGER: - return kPointerDeviceKindTouch; - case MotionEvent.TOOL_TYPE_STYLUS: - return kPointerDeviceKindStylus; - case MotionEvent.TOOL_TYPE_MOUSE: - return kPointerDeviceKindMouse; - case MotionEvent.TOOL_TYPE_ERASER: - return kPointerDeviceKindInvertedStylus; - default: - // MotionEvent.TOOL_TYPE_UNKNOWN will reach here. - return kPointerDeviceKindUnknown; - } - } - - private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer packet) { - int pointerChange = getPointerChangeForAction(event.getActionMasked()); - if (pointerChange == -1) { - return; - } - - int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex)); - - long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. - - packet.putLong(timeStamp); // time_stamp - packet.putLong(pointerChange); // change - packet.putLong(pointerKind); // kind - packet.putLong(event.getPointerId(pointerIndex)); // device - packet.putDouble(event.getX(pointerIndex)); // physical_x - packet.putDouble(event.getY(pointerIndex)); // physical_y - - if (pointerKind == kPointerDeviceKindMouse) { - packet.putLong(event.getButtonState() & 0x1F); // buttons - } else if (pointerKind == kPointerDeviceKindStylus) { - packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons - } else { - packet.putLong(0); // buttons - } - - packet.putLong(0); // obscured - - // TODO(eseidel): Could get the calibrated range if necessary: - // event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE) - packet.putDouble(event.getPressure(pointerIndex)); // pressure - packet.putDouble(0.0); // pressure_min - packet.putDouble(1.0); // pressure_max - - if (pointerKind == kPointerDeviceKindStylus) { - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance - packet.putDouble(0.0); // distance_max - } else { - packet.putDouble(0.0); // distance - packet.putDouble(0.0); // distance_max - } - - packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major - packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor - - packet.putDouble(0.0); // radius_min - packet.putDouble(0.0); // radius_max - - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation - - if (pointerKind == kPointerDeviceKindStylus) { - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt - } else { - packet.putDouble(0.0); // tilt - } + public void release() { + if (released) { + return; + } + released = true; + nativeUnregisterTexture(mNativeView.get(), id); + surfaceTexture.release(); + } + } + //------ END VIEW OVERRIDES ---- + + //----- START KEYEVENT.CALLBACK ----- + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (!isFlutterEngineAttached()) { + return super.onKeyUp(keyCode, event); + } + + Map message = new HashMap<>(); + message.put("type", "keyup"); + message.put("keymap", "android"); + encodeKeyEvent(event, message); + mFlutterKeyEventChannel.send(message); + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (!isFlutterEngineAttached()) { + return super.onKeyDown(keyCode, event); + } + + if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { + if (mLastInputConnection != null && mImm.isAcceptingText()) { + mLastInputConnection.sendKeyEvent(event); + } + } + + Map message = new HashMap<>(); + message.put("type", "keydown"); + message.put("keymap", "android"); + encodeKeyEvent(event, message); + mFlutterKeyEventChannel.send(message); + return super.onKeyDown(keyCode, event); + } + + private void encodeKeyEvent(KeyEvent event, Map message) { + message.put("flags", event.getFlags()); + message.put("codePoint", event.getUnicodeChar()); + message.put("keyCode", event.getKeyCode()); + message.put("scanCode", event.getScanCode()); + message.put("metaState", event.getMetaState()); + } + //----- END KEYEVENT.CALLBACK ----- + + //----- START METHODS INVOKED BY FRAGMENT ----- + public FlutterNativeView getFlutterNativeView() { + return mNativeView; + } + + public FlutterPluginRegistry getPluginRegistry() { + return mNativeView.getPluginRegistry(); + } + //----- END METHODS INVOKED BY FRAGMENT --- + + //------ START CALLS FORWARDED FROM FRAGMENT ---- + public void onStart() { + mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); + } + + public void onPause() { + mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); + } + + public void onPostResume() { + updateAccessibilityFeatures(); + for (ActivityLifecycleListener listener : mActivityLifecycleListeners) { + listener.onPostResume(); + } + mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); + } + + public void onStop() { + mFlutterLifecycleChannel.send("AppLifecycleState.paused"); + } + + public FlutterNativeView detach() { + if (!isFlutterEngineAttached()) + return null; + if (mDiscoveryReceiver != null) { + getContext().unregisterReceiver(mDiscoveryReceiver); + } + getHolder().removeCallback(mSurfaceCallback); + mNativeView.detach(); + + FlutterNativeView view = mNativeView; + mNativeView = null; + return view; + } + + public void destroy() { + if (!isFlutterEngineAttached()) + return; + + if (mDiscoveryReceiver != null) { + getContext().unregisterReceiver(mDiscoveryReceiver); + } + + getHolder().removeCallback(mSurfaceCallback); + + mNativeView.destroy(); + mNativeView = null; + } + + public void onMemoryPressure() { + Map message = new HashMap<>(1); + message.put("type", "memoryPressure"); + mFlutterSystemChannel.send(message); + } + + public void setInitialRoute(String route) { + mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); + } + + public void pushRoute(String route) { + mFlutterNavigationChannel.invokeMethod("pushRoute", route); + } + + public void popRoute() { + mFlutterNavigationChannel.invokeMethod("popRoute", null); + } + //------ END CALLS FORWARDED FROM FRAGMENT ---- + + //------ START RUN FROM BUNDLE ----- + public void runFromBundle(FlutterRunArguments args) { + assertFlutterEngineAttached(); + onFlutterEngineWillStart(); + mNativeView.runFromBundle(args); + onFlutterEngineDidStart(); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath) { + runFromBundle(bundlePath, defaultPath, "main", false); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { + runFromBundle(bundlePath, defaultPath, entrypoint, false); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + * Parameter `reuseRuntimeController` has no effect. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { + FlutterRunArguments args = new FlutterRunArguments(); + args.bundlePath = bundlePath; + args.entrypoint = entrypoint; + args.defaultPath = defaultPath; + runFromBundle(args); + } + + private boolean isFlutterEngineAttached() { + return mNativeView != null && mNativeView.isAttached(); + } + + void assertFlutterEngineAttached() { + if (!isFlutterEngineAttached()) + throw new AssertionError("Platform view is not attached"); + } + + private void onFlutterEngineWillStart() { + resetAccessibilityTree(); + } + + private void onFlutterEngineDidStart() { + } + //------ END RUN FROM BUNDLE ---- + + //------ START ENGINE INTERACTIONS ----- + // Called by native to notify first Flutter frame rendered. + @SuppressWarnings("unused") + public void onFirstFrame() { + // Allow listeners to remove themselves when they are called. + List listeners = new ArrayList<>(mFirstFrameListeners); + for (FirstFrameListener listener : listeners) { + listener.onFirstFrame(); + } + } + + /** + * Provide a listener that will be called once when the FlutterView renders its + * first frame to the underlaying SurfaceView. + */ + public void addFirstFrameListener(FirstFrameListener listener) { + mFirstFrameListeners.add(listener); + } + + /** + * Remove an existing first frame listener. + */ + public void removeFirstFrameListener(FirstFrameListener listener) { + mFirstFrameListeners.remove(listener); + } + + /** + * Listener will be called on the Android UI thread once when Flutter renders + * the first frame. + */ + public interface FirstFrameListener { + void onFirstFrame(); + } + //------ END ENGINE INTERACTIONS ---- + + //------- START ACCESSIBILITY ------ + // Called by native to update the semantics/accessibility tree. + @SuppressWarnings("unused") + public void updateSemantics(ByteBuffer buffer, String[] strings) { + try { + if (mAccessibilityNodeProvider != null) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + mAccessibilityNodeProvider.updateSemantics(buffer, strings); + } + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception while updating semantics", ex); + } + } + + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + try { + if (mAccessibilityNodeProvider != null) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + mAccessibilityNodeProvider.updateCustomAccessibilityActions(buffer, strings); + } + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception while updating local context actions", ex); + } + } + + public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action) { + dispatchSemanticsAction(id, action, null); + } + + public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, Object args) { + if (!isFlutterEngineAttached()) + return; + ByteBuffer encodedArgs = null; + int position = 0; + if (args != null) { + encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); + position = encodedArgs.position(); + } + nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); + } + + private void updateAccessibilityFeatures() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + String transitionAnimationScale = Settings.Global.getString(getContext().getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE); + if (transitionAnimationScale != null && transitionAnimationScale.equals("0")) { + mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; + } else { + mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; + } + } + nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + } + + private void resetWillNotDraw() { + if (!mIsSoftwareRenderingEnabled) { + setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled)); + } else { + setWillNotDraw(false); + } + } + + @Override + public void onAccessibilityStateChanged(boolean enabled) { + if (enabled) { + ensureAccessibilityEnabled(); + } else { + mAccessibilityEnabled = false; + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.setAccessibilityEnabled(false); + } + nativeSetSemanticsEnabled(mNativeView.get(), false); + } + resetWillNotDraw(); + } + + /// Must match the enum defined in window.dart. + private enum AccessibilityFeature { + ACCESSIBLE_NAVIGATION(1 << 0), + INVERT_COLORS(1 << 1), // NOT SUPPORTED + DISABLE_ANIMATIONS(1 << 2); + + AccessibilityFeature(int value) { + this.value = value; + } + + final int value; + } + + // Listens to the global TRANSITION_ANIMATION_SCALE property and notifies us so + // that we can disable animations in Flutter. + private class AnimationScaleObserver extends ContentObserver { + public AnimationScaleObserver(Handler handler) { + super(handler); } @Override - public boolean onTouchEvent(MotionEvent event) { - if (!isAttached()) { - return false; - } - - // 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); - } - - // These values must match the unpacking code in hooks.dart. - final int kPointerDataFieldCount = 19; - final int kBytePerField = 8; - - int pointerCount = event.getPointerCount(); - - ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField); - packet.order(ByteOrder.LITTLE_ENDIAN); - - int maskedAction = event.getActionMasked(); - // ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN - // only apply to a single pointer, other events apply to all pointers. - if (maskedAction == MotionEvent.ACTION_UP || maskedAction == MotionEvent.ACTION_POINTER_UP - || maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN) { - addPointerForIndex(event, event.getActionIndex(), packet); - } else { - // ACTION_MOVE may not actually mean all pointers have moved - // but it's the responsibility of a later part of the system to - // ignore 0-deltas if desired. - for (int p = 0; p < pointerCount; p++) { - addPointerForIndex(event, p, packet); - } - } - - assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); - return true; + public void onChange(boolean selfChange) { + this.onChange(selfChange, null); } + // TODO(mattcarroll): getString() requires API 17. what should we do for earlier APIs? + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override - public boolean onHoverEvent(MotionEvent event) { - if (!isAttached()) { - return false; - } - - boolean handled = handleAccessibilityHoverEvent(event); - if (!handled) { - // TODO(ianh): Expose hover events to the platform, - // implementing ADD, REMOVE, etc. - } - return handled; - } - - @Override - protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { - mMetrics.physicalWidth = width; - mMetrics.physicalHeight = height; - updateViewportMetrics(); - super.onSizeChanged(width, height, oldWidth, oldHeight); - } - - @Override - public final WindowInsets onApplyWindowInsets(WindowInsets insets) { - // Status bar, left/right system insets partially obscure content (padding). - mMetrics.physicalPaddingTop = insets.getSystemWindowInsetTop(); - mMetrics.physicalPaddingRight = insets.getSystemWindowInsetRight(); - mMetrics.physicalPaddingBottom = 0; - mMetrics.physicalPaddingLeft = insets.getSystemWindowInsetLeft(); - - // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). - mMetrics.physicalViewInsetTop = 0; - mMetrics.physicalViewInsetRight = 0; - mMetrics.physicalViewInsetBottom = insets.getSystemWindowInsetBottom(); - mMetrics.physicalViewInsetLeft = 0; - updateViewportMetrics(); - return super.onApplyWindowInsets(insets); - } - - @Override - @SuppressWarnings("deprecation") - protected boolean fitSystemWindows(Rect insets) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - // Status bar, left/right system insets partially obscure content (padding). - mMetrics.physicalPaddingTop = insets.top; - mMetrics.physicalPaddingRight = insets.right; - mMetrics.physicalPaddingBottom = 0; - mMetrics.physicalPaddingLeft = insets.left; - - // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). - mMetrics.physicalViewInsetTop = 0; - mMetrics.physicalViewInsetRight = 0; - mMetrics.physicalViewInsetBottom = insets.bottom; - mMetrics.physicalViewInsetLeft = 0; - updateViewportMetrics(); - return true; - } else { - return super.fitSystemWindows(insets); - } - } - - private boolean isAttached() { - return mNativeView != null && mNativeView.isAttached(); - } - - void assertAttached() { - if (!isAttached()) - throw new AssertionError("Platform view is not attached"); - } - - private void preRun() { - resetAccessibilityTree(); - } - - private void postRun() { - } - - public void runFromBundle(FlutterRunArguments args) { - assertAttached(); - preRun(); - mNativeView.runFromBundle(args); - postRun(); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath) { - runFromBundle(bundlePath, defaultPath, "main", false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { - runFromBundle(bundlePath, defaultPath, entrypoint, false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = bundlePath; - args.entrypoint = entrypoint; - args.defaultPath = defaultPath; - runFromBundle(args); - } - - /** - * Return the most recent frame as a bitmap. - * - * @return A bitmap. - */ - public Bitmap getBitmap() { - assertAttached(); - return nativeGetBitmap(mNativeView.get()); - } - - private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface, - int backgroundColor); - - private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, int height); - - private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); - - private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, float devicePixelRatio, - int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, - int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, - int physicalViewInsetBottom, int physicalViewInsetLeft); - - private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); - - private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, - int position); - - private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action, - ByteBuffer args, int argsPosition); - - private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); - - private static native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); - - private static native boolean nativeGetIsSoftwareRenderingEnabled(); - - private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, - SurfaceTexture surfaceTexture); - - private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); - - private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); - - private void updateViewportMetrics() { - if (!isAttached()) - return; - nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, - mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, - mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, - mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); - - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - float fps = wm.getDefaultDisplay().getRefreshRate(); - VsyncWaiter.refreshPeriodNanos = (long) (1000000000.0 / fps); - } - - // Called by native to update the semantics/accessibility tree. - public void updateSemantics(ByteBuffer buffer, String[] strings) { - try { - if (mAccessibilityNodeProvider != null) { - buffer.order(ByteOrder.LITTLE_ENDIAN); - mAccessibilityNodeProvider.updateSemantics(buffer, strings); - } - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception while updating semantics", ex); - } - } - - public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - try { - if (mAccessibilityNodeProvider != null) { - buffer.order(ByteOrder.LITTLE_ENDIAN); - mAccessibilityNodeProvider.updateCustomAccessibilityActions(buffer, strings); - } - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception while updating local context actions", ex); - } - } - - // Called by native to notify first Flutter frame rendered. - public void onFirstFrame() { - // Allow listeners to remove themselves when they are called. - List listeners = new ArrayList<>(mFirstFrameListeners); - for (FirstFrameListener listener : listeners) { - listener.onFirstFrame(); - } - } - - // ACCESSIBILITY - - private boolean mAccessibilityEnabled = false; - private boolean mTouchExplorationEnabled = false; - private int mAccessibilityFeatureFlags = 0; - private TouchExplorationListener mTouchExplorationListener; - - public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action) { - dispatchSemanticsAction(id, action, null); - } - - public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, Object args) { - if (!isAttached()) - return; - ByteBuffer encodedArgs = null; - int position = 0; - if (args != null) { - encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); - position = encodedArgs.position(); - } - nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); - } - + public void onChange(boolean selfChange, Uri uri) { + String value = Settings.Global.getString(getContext().getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE); + if (value == "0") { + mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; + } else { + mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; + } + nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + } + } + + // TODO: TouchExplorationStateChangeListener requires API 19. What do we do about earlier APIs? + @SuppressLint("NewApi") + class TouchExplorationListener implements AccessibilityManager.TouchExplorationStateChangeListener { @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mAccessibilityEnabled = mAccessibilityManager.isEnabled(); - mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); - getContext().getContentResolver().registerContentObserver(transitionUri, false, mAnimationScaleObserver); - } - - if (mAccessibilityEnabled || mTouchExplorationEnabled) { - ensureAccessibilityEnabled(); - } - if (mTouchExplorationEnabled) { - mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - } - // Apply additional accessibility settings - updateAccessibilityFeatures(); - resetWillNotDraw(); - mAccessibilityManager.addAccessibilityStateChangeListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (mTouchExplorationListener == null) { - mTouchExplorationListener = new TouchExplorationListener(); - } - mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener); - } - } - - private void updateAccessibilityFeatures() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - String transitionAnimationScale = Settings.Global.getString(getContext().getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE); - if (transitionAnimationScale != null && transitionAnimationScale.equals("0")) { - mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; - } else { - mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; - } + public void onTouchExplorationStateChanged(boolean enabled) { + if (enabled) { + mTouchExplorationEnabled = true; + ensureAccessibilityEnabled(); + mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; + nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + } else { + mTouchExplorationEnabled = false; + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.handleTouchExplorationExit(); } + mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + } + resetWillNotDraw(); } + } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - getContext().getContentResolver().unregisterContentObserver(mAnimationScaleObserver); - mAccessibilityManager.removeAccessibilityStateChangeListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener); - } - } + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + if (mAccessibilityEnabled) + return mAccessibilityNodeProvider; + // TODO(goderbauer): when a11y is off this should return a one-off snapshot of + // the a11y + // tree. + return null; + } - private void resetWillNotDraw() { - if (!mIsSoftwareRenderingEnabled) { - setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled)); - } else { - setWillNotDraw(false); - } - } + private AccessibilityBridge mAccessibilityNodeProvider; - @Override - public void onAccessibilityStateChanged(boolean enabled) { - if (enabled) { - ensureAccessibilityEnabled(); - } else { - mAccessibilityEnabled = false; - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setAccessibilityEnabled(false); - } - nativeSetSemanticsEnabled(mNativeView.get(), false); - } - resetWillNotDraw(); + void ensureAccessibilityEnabled() { + if (!isFlutterEngineAttached()) + return; + mAccessibilityEnabled = true; + if (mAccessibilityNodeProvider == null) { + mAccessibilityNodeProvider = new AccessibilityBridge(this); } + nativeSetSemanticsEnabled(mNativeView.get(), true); + mAccessibilityNodeProvider.setAccessibilityEnabled(true); + } - /// Must match the enum defined in window.dart. - private enum AccessibilityFeature { - ACCESSIBLE_NAVIGATION(1 << 0), - INVERT_COLORS(1 << 1), // NOT SUPPORTED - DISABLE_ANIMATIONS(1 << 2); - - AccessibilityFeature(int value) { - this.value = value; - } - - final int value; + void resetAccessibilityTree() { + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.reset(); } + } - // Listens to the global TRANSITION_ANIMATION_SCALE property and notifies us so - // that we can disable animations in Flutter. - private class AnimationScaleObserver extends ContentObserver { - public AnimationScaleObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - this.onChange(selfChange, null); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - String value = Settings.Global.getString(getContext().getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE); - if (value == "0") { - mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; - } else { - mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; - } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } + private boolean handleAccessibilityHoverEvent(MotionEvent event) { + if (!mTouchExplorationEnabled) { + return false; } - - class TouchExplorationListener implements AccessibilityManager.TouchExplorationStateChangeListener { - @Override - public void onTouchExplorationStateChanged(boolean enabled) { - if (enabled) { - mTouchExplorationEnabled = true; - ensureAccessibilityEnabled(); - mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } else { - mTouchExplorationEnabled = false; - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.handleTouchExplorationExit(); - } - mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } - resetWillNotDraw(); - } + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { + mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY()); + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + mAccessibilityNodeProvider.handleTouchExplorationExit(); + } else { + Log.d("flutter", "unexpected accessibility hover event: " + event); + return false; } + return true; + } + //------- END ACCESSIBILITY ---- - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider() { - if (mAccessibilityEnabled) - return mAccessibilityNodeProvider; - // TODO(goderbauer): when a11y is off this should return a one-off snapshot of - // the a11y - // tree. - return null; - } + //------- START ISOLATE METHOD CHANNEL COMMS ------ + @Override + public void setMessageHandler(String channel, BinaryMessageHandler handler) { + mNativeView.setMessageHandler(channel, handler); + } - private AccessibilityBridge mAccessibilityNodeProvider; + @Override + public void send(String channel, ByteBuffer message) { + send(channel, message, null); + } - void ensureAccessibilityEnabled() { - if (!isAttached()) - return; - mAccessibilityEnabled = true; - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = new AccessibilityBridge(this); - } - nativeSetSemanticsEnabled(mNativeView.get(), true); - mAccessibilityNodeProvider.setAccessibilityEnabled(true); + @Override + public void send(String channel, ByteBuffer message, BinaryReply callback) { + if (!isFlutterEngineAttached()) { + Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); + return; } + mNativeView.send(channel, message, callback); + } + //------- END ISOLATE METHOD CHANNEL COMMS ----- - void resetAccessibilityTree() { - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.reset(); - } - } + //------ START JNI CALLS ----- + private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface, + int backgroundColor); - private boolean handleAccessibilityHoverEvent(MotionEvent event) { - if (!mTouchExplorationEnabled) { - return false; - } - if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { - mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY()); - } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { - mAccessibilityNodeProvider.handleTouchExplorationExit(); - } else { - Log.d("flutter", "unexpected accessibility hover event: " + event); - return false; - } - return true; - } + private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, int height); - @Override - public void send(String channel, ByteBuffer message) { - send(channel, message, null); - } + private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); - @Override - public void send(String channel, ByteBuffer message, BinaryReply callback) { - if (!isAttached()) { - Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); - return; - } - mNativeView.send(channel, message, callback); - } + private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, float devicePixelRatio, + int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, + int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, + int physicalViewInsetBottom, int physicalViewInsetLeft); - @Override - public void setMessageHandler(String channel, BinaryMessageHandler handler) { - mNativeView.setMessageHandler(channel, handler); - } + private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); - /** - * Broadcast receiver used to discover active Flutter instances. - * - * This is used by the `flutter` tool to find the observatory ports for all the - * active Flutter views. We dump the data to the logs and the tool scrapes the - * log lines for the data. - */ - private class DiscoveryReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - URI observatoryUri = URI.create(FlutterNativeView.getObservatoryUri()); - JSONObject discover = new JSONObject(); - try { - discover.put("id", getContext().getPackageName()); - discover.put("observatoryPort", observatoryUri.getPort()); - Log.i(TAG, "DISCOVER: " + discover); // The tool looks for this data. See - // android_device.dart. - } catch (JSONException e) { - } - } - } + private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, + int position); - /** - * Listener will be called on the Android UI thread once when Flutter renders - * the first frame. - */ - public interface FirstFrameListener { - void onFirstFrame(); - } + private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action, + ByteBuffer args, int argsPosition); - @Override - public SurfaceTextureEntry createSurfaceTexture() { - final SurfaceTexture surfaceTexture = new SurfaceTexture(0); - surfaceTexture.detachFromGLContext(); - final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), - surfaceTexture); - nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); - return entry; - } + private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); - final class SurfaceTextureRegistryEntry implements SurfaceTextureEntry { - private final long id; - private final SurfaceTexture surfaceTexture; - private boolean released; - - SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { - this.id = id; - this.surfaceTexture = surfaceTexture; - this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture texture) { - nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); - } - }); - } + private static native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); - @Override - public SurfaceTexture surfaceTexture() { - return surfaceTexture; - } + private static native boolean nativeGetIsSoftwareRenderingEnabled(); - @Override - public long id() { - return id; - } + private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, + SurfaceTexture surfaceTexture); - @Override - public void release() { - if (released) { - return; - } - released = true; - nativeUnregisterTexture(mNativeView.get(), id); - surfaceTexture.release(); - } - } + private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + + private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); + //------ END JNI CALLS ----- } diff --git a/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java b/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java index c03992b2f9097..94de6dca1ae32 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java @@ -4,6 +4,7 @@ package io.flutter.embedding.legacy; +import android.annotation.TargetApi; import android.app.Activity; import android.graphics.Rect; import android.opengl.Matrix; @@ -14,21 +15,14 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.embedding.FlutterView; +import java.nio.ByteBuffer; +import java.util.*; + +@TargetApi(Build.VERSION_CODES.KITKAT) public class AccessibilityBridge extends AccessibilityNodeProvider implements BasicMessageChannel.MessageHandler { private static final String TAG = "FlutterView"; @@ -56,7 +50,7 @@ public class AccessibilityBridge private final BasicMessageChannel mFlutterAccessibilityChannel; - enum Action { + public enum Action { TAP(1 << 0), LONG_PRESS(1 << 1), SCROLL_LEFT(1 << 2), @@ -83,7 +77,7 @@ enum Action { this.value = value; } - final int value; + public final int value; } enum Flag { @@ -114,7 +108,7 @@ enum Flag { final int value; } - AccessibilityBridge(FlutterView owner) { + public AccessibilityBridge(FlutterView owner) { assert owner != null; mOwner = owner; mObjects = new HashMap(); @@ -125,7 +119,7 @@ enum Flag { mDecorView = ((Activity) owner.getContext()).getWindow().getDecorView(); } - void setAccessibilityEnabled(boolean accessibilityEnabled) { + public void setAccessibilityEnabled(boolean accessibilityEnabled) { mAccessibilityEnabled = accessibilityEnabled; if (accessibilityEnabled) { mFlutterAccessibilityChannel.setMessageHandler(this); @@ -579,14 +573,14 @@ private CustomAccessibilityAction getOrCreateAction(int id) { return action; } - void handleTouchExplorationExit() { + public void handleTouchExplorationExit() { if (mHoveredObject != null) { sendAccessibilityEvent(mHoveredObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); mHoveredObject = null; } } - void handleTouchExploration(float x, float y) { + public void handleTouchExploration(float x, float y) { if (mObjects.isEmpty()) { return; } @@ -603,7 +597,7 @@ void handleTouchExploration(float x, float y) { } } - void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { ArrayList updatedActions = new ArrayList(); while (buffer.hasRemaining()) { @@ -617,7 +611,7 @@ void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { } } - void updateSemantics(ByteBuffer buffer, String[] strings) { + public void updateSemantics(ByteBuffer buffer, String[] strings) { ArrayList updated = new ArrayList(); while (buffer.hasRemaining()) { int id = buffer.getInt(); @@ -898,7 +892,7 @@ private void willRemoveSemanticsObject(SemanticsObject object) { } } - void reset() { + public void reset() { mObjects.clear(); if (mA11yFocusedObject != null) sendAccessibilityEvent(mA11yFocusedObject.id, diff --git a/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java b/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java index 3319281c0a1f4..c5b32c39a1e60 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java +++ b/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java @@ -8,16 +8,15 @@ import android.content.Context; import android.content.res.AssetManager; import android.util.Log; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.view.FlutterRunArguments; +import io.flutter.embedding.FlutterView; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.view.FlutterRunArguments; -import io.flutter.embedding.FlutterView; - public class FlutterNativeView implements BinaryMessenger { private static final String TAG = "FlutterNativeView"; diff --git a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java index 7069c97c84ca9..d6347406a243f 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java @@ -7,17 +7,16 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.view.FlutterMain; +import io.flutter.embedding.FlutterView; +import io.flutter.view.TextureRegistry; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.view.FlutterMain; -import io.flutter.embedding.FlutterView; -import io.flutter.view.TextureRegistry; - public class FlutterPluginRegistry implements PluginRegistry, PluginRegistry.RequestPermissionsResultListener, diff --git a/shell/platform/android/io/flutter/embedding/legacy/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/embedding/legacy/InputConnectionAdaptor.java index a05febd397f39..1c3e94ef66c6e 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/embedding/legacy/InputConnectionAdaptor.java @@ -4,20 +4,22 @@ package io.flutter.embedding.legacy; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.text.Editable; import android.text.Selection; import android.view.KeyEvent; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.embedding.FlutterView; import java.util.Arrays; import java.util.HashMap; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.view.FlutterView; - +@TargetApi(Build.VERSION_CODES.CUPCAKE) class InputConnectionAdaptor extends BaseInputConnection { private final FlutterView mFlutterView; private final int mClient; diff --git a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewFactory.java b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewFactory.java index ba96316e1031f..b4c6bc4acbdc1 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewFactory.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewFactory.java @@ -5,7 +5,6 @@ package io.flutter.embedding.legacy; import android.content.Context; - import io.flutter.plugin.common.MessageCodec; import io.flutter.plugin.platform.PlatformView; diff --git a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewRegistry.java index 67fc5acf5435a..382afa7b47c47 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewRegistry.java @@ -4,7 +4,6 @@ package io.flutter.embedding.legacy; - /** * Registry for platform view factories. *

diff --git a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java index e5b8ceeea6bdd..608687faf2e72 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java @@ -9,6 +9,11 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.StandardMethodCodec; +import io.flutter.embedding.FlutterView; +import io.flutter.view.TextureRegistry; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -16,12 +21,6 @@ import java.util.List; import java.util.Map; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.StandardMethodCodec; -import io.flutter.embedding.FlutterView; -import io.flutter.view.TextureRegistry; - import static android.view.MotionEvent.PointerCoords; import static android.view.MotionEvent.PointerProperties; @@ -220,6 +219,7 @@ public void run() { ); } + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void onTouch(MethodCall call, MethodChannel.Result result) { List args = call.arguments(); @@ -318,6 +318,7 @@ private static List parsePointerPropertiesList(Object rawProp return pointerProperties; } + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @SuppressWarnings("unchecked") private static PointerProperties parsePointerProperties(Object rawProperties) { List propertiesList = (List) rawProperties; @@ -337,6 +338,7 @@ private static List parsePointerCoordsList(Object rawCoordsList, return pointerCoords; } + @TargetApi(Build.VERSION_CODES.GINGERBREAD) @SuppressWarnings("unchecked") private static PointerCoords parsePointerCoords(Object rawCoords, float density) { List coordsList = (List) rawCoords; diff --git a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java index 8e229d1643d25..d64de9fd89603 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java @@ -7,7 +7,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; - import io.flutter.plugin.common.BinaryMessenger; import io.flutter.embedding.FlutterView; import io.flutter.view.TextureRegistry; diff --git a/shell/platform/android/io/flutter/embedding/legacy/SingleViewPresentation.java b/shell/platform/android/io/flutter/embedding/legacy/SingleViewPresentation.java index b684e40190bb1..161f3f24e48be 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/embedding/legacy/SingleViewPresentation.java @@ -12,20 +12,15 @@ import android.os.Build; import android.os.Bundle; import android.util.Log; -import android.view.Display; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; +import android.view.*; import android.widget.FrameLayout; +import io.flutter.plugin.platform.PlatformView; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import io.flutter.plugin.platform.PlatformView; - import static android.content.Context.WINDOW_SERVICE; /* diff --git a/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java b/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java index 6cd34ff836792..8be257bd63113 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java @@ -4,7 +4,9 @@ package io.flutter.embedding.legacy; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.text.Editable; import android.text.InputType; import android.text.Selection; @@ -12,21 +14,20 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import io.flutter.plugin.common.JSONMethodCodec; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.view.FlutterView; +import io.flutter.embedding.FlutterView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; /** * Android implementation of the text input plugin. */ +@TargetApi(Build.VERSION_CODES.CUPCAKE) public class TextInputPlugin implements MethodCallHandler { private final FlutterView mView; private final InputMethodManager mImm; @@ -160,8 +161,8 @@ public InputConnection createInputConnection(FlutterView view, EditorInfo outAtt } outAttrs.imeOptions |= enterAction; - io.flutter.embedding.legacy.InputConnectionAdaptor connection = - new io.flutter.embedding.legacy.InputConnectionAdaptor(view, mClient, mFlutterChannel, mEditable); + InputConnectionAdaptor connection = + new InputConnectionAdaptor(view, mClient, mFlutterChannel, mEditable); outAttrs.initialSelStart = Selection.getSelectionStart(mEditable); outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable); diff --git a/shell/platform/android/io/flutter/embedding/legacy/VirtualDisplayController.java b/shell/platform/android/io/flutter/embedding/legacy/VirtualDisplayController.java index 9e59cfb7d25bd..516028ca6ca7a 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/VirtualDisplayController.java +++ b/shell/platform/android/io/flutter/embedding/legacy/VirtualDisplayController.java @@ -13,7 +13,6 @@ import android.view.Surface; import android.view.View; import android.view.ViewTreeObserver; - import io.flutter.plugin.platform.PlatformView; @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) @@ -54,7 +53,7 @@ public static VirtualDisplayController create( private final int mDensityDpi; private final SurfaceTexture mSurfaceTexture; private VirtualDisplay mVirtualDisplay; - private io.flutter.embedding.legacy.SingleViewPresentation mPresentation; + private SingleViewPresentation mPresentation; private Surface mSurface; @@ -72,13 +71,13 @@ private VirtualDisplayController( mContext = context; mVirtualDisplay = virtualDisplay; mDensityDpi = context.getResources().getDisplayMetrics().densityDpi; - mPresentation = new io.flutter.embedding.legacy.SingleViewPresentation( + mPresentation = new SingleViewPresentation( context, mVirtualDisplay.getDisplay(), viewFactory, viewId, createParams); mPresentation.show(); } public void resize(final int width, final int height, final Runnable onNewSizeFrameAvailable) { - final io.flutter.embedding.legacy.SingleViewPresentation.PresentationState presentationState = mPresentation.detachState(); + final SingleViewPresentation.PresentationState presentationState = mPresentation.detachState(); // We detach the surface to prevent it being destroyed when releasing the vd. // // setSurface is only available starting API 20. We could support API 19 by re-creating a new @@ -123,7 +122,7 @@ public void run() { public void onViewDetachedFromWindow(View v) {} }); - mPresentation = new io.flutter.embedding.legacy.SingleViewPresentation(mContext, mVirtualDisplay.getDisplay(), presentationState); + mPresentation = new SingleViewPresentation(mContext, mVirtualDisplay.getDisplay(), presentationState); mPresentation.show(); } From a875ffbbe53ef656772c72bd4d727bf95eca6ef7 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Mon, 3 Sep 2018 23:45:37 -0700 Subject: [PATCH 02/11] Moved BinaryMessenger out of FlutterView and into FlutterEngine, moved native calls from FlutterView to FlutterEngine. --- .../io/flutter/embedding/FlutterEngine.java | 96 ++++++ .../io/flutter/embedding/FlutterFragment.java | 28 +- .../io/flutter/embedding/FlutterView.java | 294 ++++++++---------- 3 files changed, 227 insertions(+), 191 deletions(-) create mode 100644 shell/platform/android/io/flutter/embedding/FlutterEngine.java diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java new file mode 100644 index 0000000000000..320df9704d913 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -0,0 +1,96 @@ +package io.flutter.embedding; + +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.support.annotation.NonNull; +import android.view.Surface; +import io.flutter.embedding.legacy.FlutterNativeView; +import io.flutter.embedding.legacy.FlutterPluginRegistry; +import io.flutter.embedding.legacy.PluginRegistry; +import io.flutter.plugin.common.BinaryMessenger; + +import java.nio.ByteBuffer; + +public class FlutterEngine implements BinaryMessenger { + private FlutterNativeView nativeView; + private FlutterPluginRegistry pluginRegistry; + + FlutterEngine(@NonNull FlutterNativeView nativeView, @NonNull FlutterPluginRegistry pluginRegistry) { + this.nativeView = nativeView; + this.pluginRegistry = pluginRegistry; + } + + //------- START PLUGINS ------ + public FlutterPluginRegistry getPluginRegistry() { + return pluginRegistry; + } + //------- END PLUGINS ----- + + //------- START ISOLATE METHOD CHANNEL COMMS ------ + @Override + public void setMessageHandler(String channel, BinaryMessenger.BinaryMessageHandler handler) { + // TODO: do we need to be attached for this? + nativeView.setMessageHandler(channel, handler); + } + + @Override + public void send(String channel, ByteBuffer message) { + send(channel, message, null); + } + + @Override + public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply callback) { + // TODO: ensure we're attached. + nativeView.send(channel, message, callback); + } + //------- END ISOLATE METHOD CHANNEL COMMS ----- + + //------ START JNI CALLS ----- + public native void nativeSurfaceCreated(long nativePlatformViewAndroid, + Surface surface, + int backgroundColor); + + public native void nativeSurfaceChanged(long nativePlatformViewAndroid, + int width, + int height); + + public native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + + public native void nativeSetViewportMetrics(long nativePlatformViewAndroid, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft); + + public native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + + public native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, + ByteBuffer buffer, + int position); + + public native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, + int id, + int action, + ByteBuffer args, + int argsPosition); + + public native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + + public native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + + public native boolean nativeGetIsSoftwareRenderingEnabled(); + + public native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture); + + public native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + + public native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); + //------ END JNI CALLS ----- +} diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index fc2391a1ba2c6..580bc00aaf700 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -26,8 +26,8 @@ import android.view.WindowManager; import android.widget.FrameLayout; import io.flutter.app.BuildConfig; -import io.flutter.embedding.legacy.PluginRegistry; import io.flutter.embedding.legacy.FlutterNativeView; +import io.flutter.embedding.legacy.FlutterPluginRegistry; /** * {@code Fragment} which displays a {@link FlutterView} that takes up all available space. @@ -60,7 +60,7 @@ */ @SuppressWarnings("WeakerAccess") @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) -public class FlutterFragment extends Fragment implements PluginRegistry { +public class FlutterFragment extends Fragment { private static final String TAG = "FlutterFragment"; private static final String ARG_IS_SPLASH_SCREEN_DESIRED = "show_splash_screen"; @@ -84,6 +84,7 @@ public static FlutterFragment newInstance(boolean isSplashScreenDesired, return frag; } + private FlutterEngine flutterEngine; private FrameLayout container; private FlutterView flutterView; private View launchView; @@ -131,7 +132,7 @@ public void onDestroy() { super.onDestroy(); // TODO(mattcarroll): re-evaluate how Flutter plugins interact with FlutterView and FlutterNativeView - final boolean detach = flutterView.getPluginRegistry().onViewDestroy( + final boolean detach = flutterEngine.getPluginRegistry().onViewDestroy( flutterView.getFlutterNativeView() ); if (detach || retainFlutterIsolateAfterFragmentDestruction()) { @@ -234,7 +235,8 @@ private void createLayout() { @NonNull protected FlutterView createFlutterView(@NonNull Activity activity) { FlutterNativeView nativeView = createFlutterNativeView(activity); - return new FlutterView(activity, null, nativeView); + flutterEngine = new FlutterEngine(nativeView, new FlutterPluginRegistry(nativeView, getContextCompat())); + return new FlutterView(activity, null, nativeView, flutterEngine); } /** @@ -394,24 +396,6 @@ private String getAppBundlePath() { return getArguments().getString(ARG_APP_BUNDLE_PATH); } - @Override - public final boolean hasPlugin(@NonNull String key) { - return flutterView.getPluginRegistry().hasPlugin(key); - } - - @Override - @Nullable - @SuppressWarnings("unchecked") - public T valuePublishedByPlugin(@NonNull String pluginKey) { - return (T) flutterView.getPluginRegistry().valuePublishedByPlugin(pluginKey); - } - - @Override - @NonNull - public PluginRegistry.Registrar registrarFor(@NonNull String pluginKey) { - return flutterView.getPluginRegistry().registrarFor(pluginKey); - } - /** * Should the Flutter isolate that is connected to this {@code FlutterFragment} * be retained after this {@code FlutterFragment} is destroyed? diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index 21af5c1e296ba..c9508b94843b0 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -14,7 +14,6 @@ import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.database.ContentObserver; -import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.net.Uri; @@ -54,7 +53,6 @@ */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class FlutterView extends SurfaceView implements - BinaryMessenger, TextureRegistry, AccessibilityManager.AccessibilityStateChangeListener { @@ -108,6 +106,7 @@ static final class ViewportMetrics { private final List mFirstFrameListeners; private final AtomicLong nextTextureId = new AtomicLong(0L); private FlutterNativeView mNativeView; + private FlutterEngine flutterEngine; private final AnimationScaleObserver mAnimationScaleObserver; private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not private InputConnection mLastInputConnection; @@ -124,27 +123,29 @@ public FlutterView(Context context) { } public FlutterView(Context context, AttributeSet attrs) { - this(context, attrs, null); + this(context, attrs, null, null); } - public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { + public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView, FlutterEngine flutterEngine) { super(context, attrs); - mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled(); - mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); - mMetrics = new ViewportMetrics(); - mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; - setFocusable(true); - setFocusableInTouchMode(true); - Activity activity = (Activity) getContext(); if (nativeView == null) { mNativeView = new FlutterNativeView(activity.getApplicationContext()); + this.flutterEngine = new FlutterEngine(mNativeView, new FlutterPluginRegistry(mNativeView, getContext())); } else { mNativeView = nativeView; + this.flutterEngine = flutterEngine; } mNativeView.attachViewAndActivity(this, activity); + mIsSoftwareRenderingEnabled = flutterEngine.nativeGetIsSoftwareRenderingEnabled(); + mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); + mMetrics = new ViewportMetrics(); + mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + setFocusable(true); + setFocusableInTouchMode(true); + int color = 0xFF000000; TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true); @@ -158,19 +159,19 @@ public FlutterView(Context context, AttributeSet attrs, FlutterNativeView native @Override public void surfaceCreated(SurfaceHolder holder) { assertFlutterEngineAttached(); - nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor); + flutterEngine.nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { assertFlutterEngineAttached(); - nativeSurfaceChanged(mNativeView.get(), width, height); + flutterEngine.nativeSurfaceChanged(mNativeView.get(), width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { assertFlutterEngineAttached(); - nativeSurfaceDestroyed(mNativeView.get()); + flutterEngine.nativeSurfaceDestroyed(mNativeView.get()); } }; getHolder().addCallback(mSurfaceCallback); @@ -181,15 +182,15 @@ public void surfaceDestroyed(SurfaceHolder holder) { mFirstFrameListeners = new ArrayList<>(); // Configure the platform plugins and flutter channels. - mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE); - mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE); - mFlutterKeyEventChannel = new BasicMessageChannel<>(this, "flutter/keyevent", JSONMessageCodec.INSTANCE); - mFlutterLifecycleChannel = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE); - mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system", JSONMessageCodec.INSTANCE); - mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings", JSONMessageCodec.INSTANCE); + mFlutterLocalizationChannel = new MethodChannel(flutterEngine, "flutter/localization", JSONMethodCodec.INSTANCE); + mFlutterNavigationChannel = new MethodChannel(flutterEngine, "flutter/navigation", JSONMethodCodec.INSTANCE); + mFlutterKeyEventChannel = new BasicMessageChannel<>(flutterEngine, "flutter/keyevent", JSONMessageCodec.INSTANCE); + mFlutterLifecycleChannel = new BasicMessageChannel<>(flutterEngine, "flutter/lifecycle", StringCodec.INSTANCE); + mFlutterSystemChannel = new BasicMessageChannel<>(flutterEngine, "flutter/system", JSONMessageCodec.INSTANCE); + mFlutterSettingsChannel = new BasicMessageChannel<>(flutterEngine, "flutter/settings", JSONMessageCodec.INSTANCE); PlatformPlugin platformPlugin = new PlatformPlugin(activity); - MethodChannel flutterPlatformChannel = new MethodChannel(this, "flutter/platform", JSONMethodCodec.INSTANCE); + MethodChannel flutterPlatformChannel = new MethodChannel(flutterEngine, "flutter/platform", JSONMethodCodec.INSTANCE); flutterPlatformChannel.setMethodCallHandler(platformPlugin); addActivityLifecycleListener(platformPlugin); mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -338,7 +339,7 @@ public boolean onTouchEvent(MotionEvent event) { } assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + flutterEngine.nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); return true; } @@ -505,10 +506,21 @@ protected boolean fitSystemWindows(Rect insets) { private void updateViewportMetrics() { if (!isFlutterEngineAttached()) return; - nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, - mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, - mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, - mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); + + flutterEngine.nativeSetViewportMetrics( + mNativeView.get(), + mMetrics.devicePixelRatio, + mMetrics.physicalWidth, + mMetrics.physicalHeight, + mMetrics.physicalPaddingTop, + mMetrics.physicalPaddingRight, + mMetrics.physicalPaddingBottom, + mMetrics.physicalPaddingLeft, + mMetrics.physicalViewInsetTop, + mMetrics.physicalViewInsetRight, + mMetrics.physicalViewInsetBottom, + mMetrics.physicalViewInsetLeft + ); WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); float fps = wm.getDefaultDisplay().getRefreshRate(); @@ -523,7 +535,7 @@ public SurfaceTextureEntry createSurfaceTexture() { surfaceTexture.detachFromGLContext(); final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture); - nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); + flutterEngine.nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); return entry; } @@ -538,7 +550,7 @@ final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextur this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture texture) { - nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); + flutterEngine.nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); } }); } @@ -559,7 +571,7 @@ public void release() { return; } released = true; - nativeUnregisterTexture(mNativeView.get(), id); + flutterEngine.nativeUnregisterTexture(mNativeView.get(), id); surfaceTexture.release(); } } @@ -687,98 +699,6 @@ public void popRoute() { } //------ END CALLS FORWARDED FROM FRAGMENT ---- - //------ START RUN FROM BUNDLE ----- - public void runFromBundle(FlutterRunArguments args) { - assertFlutterEngineAttached(); - onFlutterEngineWillStart(); - mNativeView.runFromBundle(args); - onFlutterEngineDidStart(); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath) { - runFromBundle(bundlePath, defaultPath, "main", false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { - runFromBundle(bundlePath, defaultPath, entrypoint, false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = bundlePath; - args.entrypoint = entrypoint; - args.defaultPath = defaultPath; - runFromBundle(args); - } - - private boolean isFlutterEngineAttached() { - return mNativeView != null && mNativeView.isAttached(); - } - - void assertFlutterEngineAttached() { - if (!isFlutterEngineAttached()) - throw new AssertionError("Platform view is not attached"); - } - - private void onFlutterEngineWillStart() { - resetAccessibilityTree(); - } - - private void onFlutterEngineDidStart() { - } - //------ END RUN FROM BUNDLE ---- - - //------ START ENGINE INTERACTIONS ----- - // Called by native to notify first Flutter frame rendered. - @SuppressWarnings("unused") - public void onFirstFrame() { - // Allow listeners to remove themselves when they are called. - List listeners = new ArrayList<>(mFirstFrameListeners); - for (FirstFrameListener listener : listeners) { - listener.onFirstFrame(); - } - } - - /** - * Provide a listener that will be called once when the FlutterView renders its - * first frame to the underlaying SurfaceView. - */ - public void addFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.add(listener); - } - - /** - * Remove an existing first frame listener. - */ - public void removeFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.remove(listener); - } - - /** - * Listener will be called on the Android UI thread once when Flutter renders - * the first frame. - */ - public interface FirstFrameListener { - void onFirstFrame(); - } - //------ END ENGINE INTERACTIONS ---- - //------- START ACCESSIBILITY ------ // Called by native to update the semantics/accessibility tree. @SuppressWarnings("unused") @@ -817,7 +737,7 @@ public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, O encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); position = encodedArgs.position(); } - nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); + flutterEngine.nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); } private void updateAccessibilityFeatures() { @@ -830,7 +750,7 @@ private void updateAccessibilityFeatures() { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } private void resetWillNotDraw() { @@ -850,7 +770,7 @@ public void onAccessibilityStateChanged(boolean enabled) { if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.setAccessibilityEnabled(false); } - nativeSetSemanticsEnabled(mNativeView.get(), false); + flutterEngine.nativeSetSemanticsEnabled(mNativeView.get(), false); } resetWillNotDraw(); } @@ -891,7 +811,7 @@ public void onChange(boolean selfChange, Uri uri) { } else { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } } @@ -904,14 +824,14 @@ public void onTouchExplorationStateChanged(boolean enabled) { mTouchExplorationEnabled = true; ensureAccessibilityEnabled(); mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } else { mTouchExplorationEnabled = false; if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.handleTouchExplorationExit(); } mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); } resetWillNotDraw(); } @@ -936,7 +856,7 @@ void ensureAccessibilityEnabled() { if (mAccessibilityNodeProvider == null) { mAccessibilityNodeProvider = new AccessibilityBridge(this); } - nativeSetSemanticsEnabled(mNativeView.get(), true); + flutterEngine.nativeSetSemanticsEnabled(mNativeView.get(), true); mAccessibilityNodeProvider.setAccessibilityEnabled(true); } @@ -962,59 +882,95 @@ private boolean handleAccessibilityHoverEvent(MotionEvent event) { } //------- END ACCESSIBILITY ---- - //------- START ISOLATE METHOD CHANNEL COMMS ------ - @Override - public void setMessageHandler(String channel, BinaryMessageHandler handler) { - mNativeView.setMessageHandler(channel, handler); + //------ START RUN FROM BUNDLE ----- + public void runFromBundle(FlutterRunArguments args) { + assertFlutterEngineAttached(); + onFlutterEngineWillStart(); + mNativeView.runFromBundle(args); + onFlutterEngineDidStart(); } - @Override - public void send(String channel, ByteBuffer message) { - send(channel, message, null); + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath) { + runFromBundle(bundlePath, defaultPath, "main", false); } - @Override - public void send(String channel, ByteBuffer message, BinaryReply callback) { - if (!isFlutterEngineAttached()) { - Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); - return; - } - mNativeView.send(channel, message, callback); + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { + runFromBundle(bundlePath, defaultPath, entrypoint, false); } - //------- END ISOLATE METHOD CHANNEL COMMS ----- - - //------ START JNI CALLS ----- - private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface, - int backgroundColor); - private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, int height); - - private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); - - private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, float devicePixelRatio, - int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, - int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, - int physicalViewInsetBottom, int physicalViewInsetLeft); - - private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + * Parameter `reuseRuntimeController` has no effect. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { + FlutterRunArguments args = new FlutterRunArguments(); + args.bundlePath = bundlePath; + args.entrypoint = entrypoint; + args.defaultPath = defaultPath; + runFromBundle(args); + } - private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, - int position); + private boolean isFlutterEngineAttached() { + return mNativeView != null && mNativeView.isAttached(); + } - private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action, - ByteBuffer args, int argsPosition); + void assertFlutterEngineAttached() { + if (!isFlutterEngineAttached()) + throw new AssertionError("Platform view is not attached"); + } - private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + private void onFlutterEngineWillStart() { + resetAccessibilityTree(); + } - private static native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + private void onFlutterEngineDidStart() { + } + //------ END RUN FROM BUNDLE ---- - private static native boolean nativeGetIsSoftwareRenderingEnabled(); + //------ START ENGINE INTERACTIONS ----- + // Called by native to notify first Flutter frame rendered. + @SuppressWarnings("unused") + public void onFirstFrame() { + // Allow listeners to remove themselves when they are called. + List listeners = new ArrayList<>(mFirstFrameListeners); + for (FirstFrameListener listener : listeners) { + listener.onFirstFrame(); + } + } - private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, - SurfaceTexture surfaceTexture); + /** + * Provide a listener that will be called once when the FlutterView renders its + * first frame to the underlaying SurfaceView. + */ + public void addFirstFrameListener(FirstFrameListener listener) { + mFirstFrameListeners.add(listener); + } - private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + /** + * Remove an existing first frame listener. + */ + public void removeFirstFrameListener(FirstFrameListener listener) { + mFirstFrameListeners.remove(listener); + } - private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); - //------ END JNI CALLS ----- + /** + * Listener will be called on the Android UI thread once when Flutter renders + * the first frame. + */ + public interface FirstFrameListener { + void onFirstFrame(); + } + //------ END ENGINE INTERACTIONS ---- } From e5a57619b9b8b24ffbf907c645028e2d67ee0cba Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 4 Sep 2018 15:02:21 -0700 Subject: [PATCH 03/11] Introduced concept of a FlutterRenderer. It doesn't have any meaningful behavior yet. Need strategy for resolving FlutterNativeView vs FlutterEngine first. --- .../io/flutter/embedding/FlutterEngine.java | 6 +++ .../io/flutter/embedding/FlutterRenderer.java | 40 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 shell/platform/android/io/flutter/embedding/FlutterRenderer.java diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java index 320df9704d913..e62a15a7de05a 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -13,13 +13,19 @@ public class FlutterEngine implements BinaryMessenger { private FlutterNativeView nativeView; + private FlutterRenderer renderer; private FlutterPluginRegistry pluginRegistry; FlutterEngine(@NonNull FlutterNativeView nativeView, @NonNull FlutterPluginRegistry pluginRegistry) { this.nativeView = nativeView; + this.renderer = new FlutterRenderer(); this.pluginRegistry = pluginRegistry; } + public FlutterRenderer getRenderer() { + return renderer; + } + //------- START PLUGINS ------ public FlutterPluginRegistry getPluginRegistry() { return pluginRegistry; diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java new file mode 100644 index 0000000000000..40fbfee8a4bd0 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -0,0 +1,40 @@ +package io.flutter.embedding; + +import android.annotation.TargetApi; +import android.os.Build; +import android.support.annotation.NonNull; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class FlutterRenderer { + + private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); + + public void attachToView() { + // TODO(mattcarroll): do whatever is needed to start rendering and processing user input + } + + public void detachFromView() { + // TODO(mattcarroll): stop rendering to surface and stop processing user input + } + + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.add(listener); + } + + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.remove(listener); + } + + private void notifyFirstFrameListeners() { + for (OnFirstFrameRenderedListener listener : firstFrameListeners) { + listener.onFirstFrameRendered(); + } + } + + public interface OnFirstFrameRenderedListener { + void onFirstFrameRendered(); + } +} From 26d8ef64dc9ce62b2acf1fc8d229edb489cdce2f Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 4 Sep 2018 15:59:58 -0700 Subject: [PATCH 04/11] Removed discovery behavior from FlutterView to match recent PR: https://github.com/flutter/engine/pull/6157 --- .../io/flutter/embedding/FlutterView.java | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index c9508b94843b0..5ee2a400a3e19 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -58,8 +58,6 @@ public class FlutterView extends SurfaceView implements private static final String TAG = "FlutterView"; - private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER"; - // Must match the PointerChange enum in pointer.dart. private static final int kPointerChangeCancel = 0; private static final int kPointerChangeAdd = 1; @@ -101,7 +99,6 @@ static final class ViewportMetrics { private final BasicMessageChannel mFlutterLifecycleChannel; private final BasicMessageChannel mFlutterSystemChannel; private final BasicMessageChannel mFlutterSettingsChannel; - private final BroadcastReceiver mDiscoveryReceiver; private final List mActivityLifecycleListeners; private final List mFirstFrameListeners; private final AtomicLong nextTextureId = new AtomicLong(0L); @@ -198,34 +195,6 @@ public void surfaceDestroyed(SurfaceHolder holder) { setLocale(getResources().getConfiguration().locale); setUserSettings(); - - if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { - mDiscoveryReceiver = new DiscoveryReceiver(); - context.registerReceiver(mDiscoveryReceiver, new IntentFilter(ACTION_DISCOVER)); - } else { - mDiscoveryReceiver = null; - } - } - - /** - * Broadcast receiver used to discover active Flutter instances. - * - * This is used by the `flutter` tool to find the observatory ports for all the - * active Flutter views. We dump the data to the logs and the tool scrapes the - * log lines for the data. - */ - private class DiscoveryReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - URI observatoryUri = URI.create(FlutterNativeView.getObservatoryUri()); - JSONObject discover = new JSONObject(); - try { - discover.put("id", getContext().getPackageName()); - discover.put("observatoryPort", observatoryUri.getPort()); - Log.i(TAG, "DISCOVER: " + discover); // The tool looks for this data. See android_device.dart. - } catch (JSONException e) { - } - } } public void addActivityLifecycleListener(ActivityLifecycleListener listener) { @@ -621,6 +590,10 @@ private void encodeKeyEvent(KeyEvent event, Map message) { } //----- END KEYEVENT.CALLBACK ----- + public FlutterEngine getFlutterEngine() { + return flutterEngine; + } + //----- START METHODS INVOKED BY FRAGMENT ----- public FlutterNativeView getFlutterNativeView() { return mNativeView; @@ -655,9 +628,6 @@ public void onStop() { public FlutterNativeView detach() { if (!isFlutterEngineAttached()) return null; - if (mDiscoveryReceiver != null) { - getContext().unregisterReceiver(mDiscoveryReceiver); - } getHolder().removeCallback(mSurfaceCallback); mNativeView.detach(); @@ -670,10 +640,6 @@ public void destroy() { if (!isFlutterEngineAttached()) return; - if (mDiscoveryReceiver != null) { - getContext().unregisterReceiver(mDiscoveryReceiver); - } - getHolder().removeCallback(mSurfaceCallback); mNativeView.destroy(); From 11103be4d5037d9ad86c95e5b046326481a717f4 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 6 Sep 2018 21:01:28 -0700 Subject: [PATCH 05/11] Replaced FlutterNativeView with FlutterEngine and FlutterRenderer. Eliminated FlutterEngine references from within FlutterView. Centralized all JNI calls within a FlutterJNI and updated the C side accordingly. --- shell/platform/android/BUILD.gn | 18 + .../io/flutter/embedding/FlutterActivity.java | 6 + .../io/flutter/embedding/FlutterEngine.java | 259 +++++-- .../io/flutter/embedding/FlutterFragment.java | 183 ++++- .../io/flutter/embedding/FlutterJNI.java | 106 +++ .../io/flutter/embedding/FlutterRenderer.java | 208 +++++- .../io/flutter/embedding/FlutterView.java | 643 +++++++----------- .../embedding/legacy/AccessibilityBridge.java | 7 +- .../embedding/legacy/FlutterNativeView.java | 261 ------- .../legacy/FlutterPluginRegistry.java | 34 +- .../legacy/PlatformViewsController.java | 33 +- .../embedding/legacy/PluginRegistry.java | 11 +- .../embedding/legacy/TextInputPlugin.java | 7 +- .../android/platform_view_android_jni.cc | 590 +++++++++++----- 14 files changed, 1389 insertions(+), 977 deletions(-) create mode 100644 shell/platform/android/io/flutter/embedding/FlutterJNI.java delete mode 100644 shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index f332a60f7a97d..1e526a5ae95e1 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -97,6 +97,24 @@ java_library("flutter_shell_java") { supports_android = true java_files = [ + "io/flutter/embedding/FlutterActivity.java", + "io/flutter/embedding/FlutterEngine.java", + "io/flutter/embedding/FlutterFragment.java", + "io/flutter/embedding/FlutterJNI.java", + "io/flutter/embedding/FlutterRenderer.java", + "io/flutter/embedding/FlutterShellArgs.java", + "io/flutter/embedding/FlutterView.java", + "io/flutter/embedding/legacy/AccessibilityBridge.java", + "io/flutter/embedding/legacy/FlutterPluginRegistry.java", + "io/flutter/embedding/legacy/InputConnectionAdaptor.java", + "io/flutter/embedding/legacy/PlatformViewFactory.java", + "io/flutter/embedding/legacy/PlatformViewRegistry.java", + "io/flutter/embedding/legacy/PlatformViewRegistryImpl.java", + "io/flutter/embedding/legacy/PlatformViewsController.java", + "io/flutter/embedding/legacy/PluginRegistry.java", + "io/flutter/embedding/legacy/SingleViewPresentation.java", + "io/flutter/embedding/legacy/TextInputPlugin.java", + "io/flutter/embedding/legacy/VirtualDisplayController.java", "io/flutter/app/FlutterActivity.java", "io/flutter/app/FlutterActivityDelegate.java", "io/flutter/app/FlutterActivityEvents.java", diff --git a/shell/platform/android/io/flutter/embedding/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/FlutterActivity.java index 4fed2df6411af..0f40e27e83458 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/FlutterActivity.java @@ -16,6 +16,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; @@ -65,6 +66,7 @@ public class FlutterActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate()"); FlutterShellArgs args = FlutterShellArgs.fromIntent(getIntent()); // TODO(mattcarroll): Change FlutterMain to accept FlutterShellArgs and move additional constants in @@ -79,6 +81,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public void onPostResume() { super.onPostResume(); + Log.d(TAG, "onPostResume()"); flutterFragment.onPostResume(); } @@ -90,6 +93,7 @@ protected void onNewIntent(Intent intent) { @Override public void onBackPressed() { + Log.d(TAG, "onBackPressed()"); flutterFragment.onBackPressed(); } @@ -107,6 +111,7 @@ public void onUserLeaveHint() { * {@code FrameLayout} will hold a {@code FlutterFragment}, which displays a {@code FlutterView}. */ private void createFlutterFragmentContainer() { + Log.d(TAG, "createFlutterFragmentContainer()"); FrameLayout container = new FrameLayout(this); container.setId(CONTAINER_ID); container.setLayoutParams(new ViewGroup.LayoutParams( @@ -122,6 +127,7 @@ private void createFlutterFragmentContainer() { * a reference to that {@code FlutterFragment} is retained in {@code flutterFragment}. */ private void createFlutterFragment() { + Log.d(TAG, "createFlutterFragment()"); FragmentManager fragmentManager = getFragmentManager(); flutterFragment = (FlutterFragment) fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT); diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java index e62a15a7de05a..3a002dfacabfc 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -1,25 +1,46 @@ package io.flutter.embedding; +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.SurfaceTexture; -import android.support.annotation.NonNull; +import android.util.Log; import android.view.Surface; -import io.flutter.embedding.legacy.FlutterNativeView; -import io.flutter.embedding.legacy.FlutterPluginRegistry; -import io.flutter.embedding.legacy.PluginRegistry; -import io.flutter.plugin.common.BinaryMessenger; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.flutter.embedding.legacy.FlutterPluginRegistry; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.view.FlutterRunArguments; public class FlutterEngine implements BinaryMessenger { - private FlutterNativeView nativeView; + private static final String TAG = "FlutterEngine"; + + private long nativeObjectReference; + private FlutterJNI flutterJNI; private FlutterRenderer renderer; private FlutterPluginRegistry pluginRegistry; - - FlutterEngine(@NonNull FlutterNativeView nativeView, @NonNull FlutterPluginRegistry pluginRegistry) { - this.nativeView = nativeView; - this.renderer = new FlutterRenderer(); - this.pluginRegistry = pluginRegistry; + private boolean isBackgroundView; // TODO(mattcarroll): rename to something without "view" + + FlutterEngine( + Context context, + Resources resources, + boolean isBackgroundView + ) { + this.flutterJNI = new FlutterJNI(); + this.resources = resources; + this.isBackgroundView = isBackgroundView; + + pluginRegistry = new FlutterPluginRegistry(this, context); + attach(); + assertAttached(); + // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling. + this.renderer = new FlutterRenderer(this, flutterJNI, nativeObjectReference); + mMessageHandlers = new HashMap<>(); } public FlutterRenderer getRenderer() { @@ -34,69 +55,197 @@ public FlutterPluginRegistry getPluginRegistry() { //------- START ISOLATE METHOD CHANNEL COMMS ------ @Override - public void setMessageHandler(String channel, BinaryMessenger.BinaryMessageHandler handler) { - // TODO: do we need to be attached for this? - nativeView.setMessageHandler(channel, handler); + public void setMessageHandler(String channel, BinaryMessageHandler handler) { + if (handler == null) { + mMessageHandlers.remove(channel); + } else { + mMessageHandlers.put(channel, handler); + } } @Override public void send(String channel, ByteBuffer message) { send(channel, message, null); } + //------- END ISOLATE METHOD CHANNEL COMMS ----- - @Override - public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply callback) { - // TODO: ensure we're attached. - nativeView.send(channel, message, callback); + //------- START COPY FROM FlutterNativeView ----- + private final Map mMessageHandlers; + private int mNextReplyId = 1; + private final Map mPendingReplies = new HashMap<>(); + + private final Resources resources; + private boolean applicationIsRunning; + + private void attach() { + nativeObjectReference = flutterJNI.nativeAttach(this, isBackgroundView); } - //------- END ISOLATE METHOD CHANNEL COMMS ----- - //------ START JNI CALLS ----- - public native void nativeSurfaceCreated(long nativePlatformViewAndroid, - Surface surface, - int backgroundColor); + public void detach() { + pluginRegistry.detach(); + flutterJNI.nativeDetach(nativeObjectReference); + } - public native void nativeSurfaceChanged(long nativePlatformViewAndroid, - int width, - int height); + public void destroy() { + pluginRegistry.destroy(); + flutterJNI.nativeDestroy(nativeObjectReference); + nativeObjectReference = 0; + applicationIsRunning = false; + } - public native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + public void attachViewAndActivity(Activity activity) { + pluginRegistry.attach(this, activity); + } - public native void nativeSetViewportMetrics(long nativePlatformViewAndroid, - float devicePixelRatio, - int physicalWidth, - int physicalHeight, - int physicalPaddingTop, - int physicalPaddingRight, - int physicalPaddingBottom, - int physicalPaddingLeft, - int physicalViewInsetTop, - int physicalViewInsetRight, - int physicalViewInsetBottom, - int physicalViewInsetLeft); + public boolean isAttached() { + return nativeObjectReference != 0; + } - public native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + public long get() { + return nativeObjectReference; + } - public native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, - ByteBuffer buffer, - int position); + public void assertAttached() { + if (!isAttached()) throw new AssertionError("Platform view is not attached"); + } - public native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, - int id, - int action, - ByteBuffer args, - int argsPosition); + public void runFromBundle(FlutterRunArguments args) { + if (args.bundlePath == null) { + throw new AssertionError("A bundlePath must be specified"); + } else if (args.entrypoint == null) { + throw new AssertionError("An entrypoint must be specified"); + } + runFromBundleInternal(args.bundlePath, args.entrypoint, args.libraryPath, args.defaultPath); + } - public native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + * Parameter `reuseRuntimeController` has no effect. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, + boolean reuseRuntimeController) { + runFromBundleInternal(bundlePath, entrypoint, null, defaultPath); + } + + private void runFromBundleInternal(String bundlePath, String entrypoint, + String libraryPath, String defaultPath) { + assertAttached(); + if (applicationIsRunning) + throw new AssertionError( + "This Flutter engine instance is already running an application"); + flutterJNI.nativeRunBundleAndSnapshotFromLibrary(nativeObjectReference, bundlePath, + defaultPath, entrypoint, libraryPath, resources.getAssets()); + + applicationIsRunning = true; + } + + public boolean isApplicationRunning() { + return applicationIsRunning; + } + + public String getObservatoryUri() { + return flutterJNI.nativeGetObservatoryUri(); + } + + @Override + public void send(String channel, ByteBuffer message, BinaryReply callback) { + if (!isAttached()) { + Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); + return; + } + + int replyId = 0; + if (callback != null) { + replyId = mNextReplyId++; + mPendingReplies.put(replyId, callback); + } + if (message == null) { + flutterJNI.nativeDispatchEmptyPlatformMessage(nativeObjectReference, channel, replyId); + } else { + flutterJNI.nativeDispatchPlatformMessage( + nativeObjectReference, channel, message, message.position(), replyId); + } + } - public native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + // Called by native to send us a platform message. + @SuppressWarnings("unused") + private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { + assertAttached(); + BinaryMessageHandler handler = mMessageHandlers.get(channel); + if (handler != null) { + try { + final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); + handler.onMessage(buffer, new BinaryReply() { + private final AtomicBoolean done = new AtomicBoolean(false); + @Override + public void reply(ByteBuffer reply) { + if (!isAttached()) { + Log.d(TAG, + "handlePlatformMessage replying to a detached view, channel=" + + channel); + return; + } + if (done.getAndSet(true)) { + throw new IllegalStateException("Reply already submitted"); + } + if (reply == null) { + flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId); + } else { + flutterJNI.nativeInvokePlatformMessageResponseCallback(nativeObjectReference, replyId, reply, reply.position()); + } + } + }); + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception in binary message listener", ex); + flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId); + } + return; + } + flutterJNI.nativeInvokePlatformMessageEmptyResponseCallback(nativeObjectReference, replyId); + } - public native boolean nativeGetIsSoftwareRenderingEnabled(); + // Called by native to respond to a platform message that we sent. + @SuppressWarnings("unused") + private void handlePlatformMessageResponse(int replyId, byte[] reply) { + BinaryReply callback = mPendingReplies.remove(replyId); + if (callback != null) { + try { + callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception in binary message reply handler", ex); + } + } + } - public native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture); + // Called by native to update the semantics/accessibility tree. + @SuppressWarnings("unused") + private void updateSemantics(ByteBuffer buffer, String[] strings) { + Log.d(TAG, "updateSemantics()"); + renderer.updateSemantics(buffer, strings); + } - public native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + // Called by native to update the custom accessibility actions. + @SuppressWarnings("unused") + private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + Log.d(TAG, "updateCustomAccessibilityActions()"); + renderer.updateCustomAccessibilityActions(buffer, strings); + } + + // Called by native to notify first Flutter frame rendered. + @SuppressWarnings("unused") + private void onFirstFrame() { + Log.d(TAG, "onFirstFrame()"); + renderer.onFirstFrameRendered(); + } - public native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); - //------ END JNI CALLS ----- + // Called by native to notify when the engine is restarted (cold reload). + @SuppressWarnings("unused") + private void onPreEngineRestart() { + if (pluginRegistry == null) + return; + pluginRegistry.onPreEngineRestart(); + } + //------- END COPY FROM FlutterNativeView ---- } diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index 580bc00aaf700..7bdbb37f1882b 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -13,6 +13,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -25,9 +26,17 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; -import io.flutter.app.BuildConfig; -import io.flutter.embedding.legacy.FlutterNativeView; -import io.flutter.embedding.legacy.FlutterPluginRegistry; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.JSONMessageCodec; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.StringCodec; +import io.flutter.plugin.platform.PlatformPlugin; +import io.flutter.view.FlutterRunArguments; /** * {@code Fragment} which displays a {@link FlutterView} that takes up all available space. @@ -87,13 +96,19 @@ public static FlutterFragment newInstance(boolean isSplashScreenDesired, private FlutterEngine flutterEngine; private FrameLayout container; private FlutterView flutterView; + private PlatformPlugin platformPlugin; + private BasicMessageChannel mFlutterLifecycleChannel; + private BasicMessageChannel mFlutterSystemChannel; + private MethodChannel mFlutterNavigationChannel; private View launchView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + Log.e(TAG, "onCreateView()"); createLayout(); - // TODO: Should we start running the FlutterView here or when attached? It was being done here + // TODO: Should we start running the FlutterView here or when attached to window? It was being done here // in the Activity, but maybe that was a problem to begin with? + flutterView.attachToFlutterRenderer(flutterEngine.getRenderer(), flutterEngine); doInitialFlutterViewRun(); return container; @@ -102,45 +117,51 @@ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle saved @Override public void onStart() { super.onStart(); - flutterView.onStart(); + Log.d(TAG, "onStart()"); + mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); } @Override public void onResume() { super.onResume(); - // TODO: should flutterView have an onResume() method? + Log.d(TAG, "onResume()"); } public void onPostResume() { - flutterView.onPostResume(); + Log.d(TAG, "onPostResume()"); + flutterView.updateAccessibilityFeatures(); + platformPlugin.onPostResume(); + mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); } @Override public void onPause() { super.onPause(); - flutterView.onPause(); + Log.d(TAG, "onPause()"); + mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); } @Override public void onStop() { super.onStop(); - flutterView.onStop(); + Log.d(TAG, "onStop()"); + mFlutterLifecycleChannel.send("AppLifecycleState.paused"); } @Override public void onDestroy() { super.onDestroy(); + Log.d(TAG, "onDestroy()"); // TODO(mattcarroll): re-evaluate how Flutter plugins interact with FlutterView and FlutterNativeView - final boolean detach = flutterEngine.getPluginRegistry().onViewDestroy( - flutterView.getFlutterNativeView() - ); + final boolean detach = flutterEngine.getPluginRegistry().onViewDestroy(flutterEngine); + flutterView.detachFromFlutterRenderer(); if (detach || retainFlutterIsolateAfterFragmentDestruction()) { // Detach, but do not destroy the FlutterView if a plugin expressed interest in its // FlutterNativeView. - flutterView.detach(); + flutterEngine.detach(); } else { - flutterView.destroy(); + flutterEngine.destroy(); } } @@ -150,7 +171,8 @@ public void onDestroy() { * See {@link Activity#onBackPressed()} */ public void onBackPressed() { - flutterView.popRoute(); + Log.d(TAG, "onBackPressed()"); + popRoute(); } /** @@ -163,7 +185,7 @@ public void onBackPressed() { * @param grantResults permission grants or denials */ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - flutterView.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults); + flutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults); } /** @@ -174,12 +196,12 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis * @param intent new Intent */ public void onNewIntent(@NonNull Intent intent) { - flutterView.getPluginRegistry().onNewIntent(intent); + flutterEngine.getPluginRegistry().onNewIntent(intent); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - flutterView.getPluginRegistry().onActivityResult(requestCode, resultCode, data); + flutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data); } /** @@ -189,7 +211,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { * See {@link Activity#onUserLeaveHint()} */ public void onUserLeaveHint() { - flutterView.getPluginRegistry().onUserLeaveHint(); + flutterEngine.getPluginRegistry().onUserLeaveHint(); } @Override @@ -199,14 +221,20 @@ public void onTrimMemory(int level) { // Use a trim level delivered while the application is running so the // framework has a chance to react to the notification. if (level == TRIM_MEMORY_RUNNING_LOW) { - flutterView.onMemoryPressure(); + sendMemoryPressureWarningToFlutter(); } } @Override public void onLowMemory() { super.onLowMemory(); - flutterView.onMemoryPressure(); + sendMemoryPressureWarningToFlutter(); + } + + private void sendMemoryPressureWarningToFlutter() { + Map message = new HashMap<>(1); + message.put("type", "memoryPressure"); + mFlutterSystemChannel.send(message); } /** @@ -215,6 +243,7 @@ public void onLowMemory() { */ private void createLayout() { container = new FrameLayout(getContextCompat()); + container.setBackgroundColor(Color.RED); container.setLayoutParams(MATCH_PARENT); flutterView = createFlutterView(getActivity()); @@ -234,23 +263,37 @@ private void createLayout() { */ @NonNull protected FlutterView createFlutterView(@NonNull Activity activity) { - FlutterNativeView nativeView = createFlutterNativeView(activity); - flutterEngine = new FlutterEngine(nativeView, new FlutterPluginRegistry(nativeView, getContextCompat())); - return new FlutterView(activity, null, nativeView, flutterEngine); + Log.d(TAG, "createFlutterView()"); + flutterEngine = createFlutterEngine(activity); + + platformPlugin = new PlatformPlugin(activity); + MethodChannel flutterPlatformChannel = new MethodChannel(flutterEngine, "flutter/platform", JSONMethodCodec.INSTANCE); + flutterPlatformChannel.setMethodCallHandler(platformPlugin); + + mFlutterLifecycleChannel = new BasicMessageChannel<>(flutterEngine, "flutter/lifecycle", StringCodec.INSTANCE); + mFlutterSystemChannel = new BasicMessageChannel<>(flutterEngine, "flutter/system", JSONMessageCodec.INSTANCE); + mFlutterNavigationChannel = new MethodChannel(flutterEngine, "flutter/navigation", JSONMethodCodec.INSTANCE); + + return new FlutterView(activity, null); } /** - * Hook for subclasses to customize the creation of the {@code FlutterNativeView}. + * Hook for subclasses to customize the creation of the {@code FlutterEngine}. * * This method is only invoked from the default implementation of {@link #createFlutterView(Activity)}. * If {@link #createFlutterView(Activity)} is overridden, then this method will not be invoked unless * it is invoked directly from the subclass. * - * By default, this method returns a standard {@link FlutterNativeView} without any modification. + * By default, this method returns a standard {@link FlutterEngine} without any modification. */ @NonNull - protected FlutterNativeView createFlutterNativeView(@NonNull Context context) { - return new FlutterNativeView(context); + protected FlutterEngine createFlutterEngine(@NonNull Context context) { + Log.d(TAG, "createFlutterEngine()"); + Activity activity = (Activity) context; + FlutterEngine flutterEngine = new FlutterEngine(activity, getResources(), false); + flutterEngine.attachViewAndActivity(activity); + + return flutterEngine; } /** @@ -288,9 +331,10 @@ private View createLaunchView() { // TODO(mattcarroll): this API version error exists in the original code. Why is it there? view.setBackground(launchScreenDrawable); - flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() { + // TODO(mattcarroll): move the launch screen support into FlutterView + flutterEngine.getRenderer().addOnFirstFrameRenderedListener(new FlutterRenderer.OnFirstFrameRenderedListener() { @Override - public void onFirstFrame() { + public void onFirstFrameRendered() { launchView.animate() .alpha(0f) // Use Android's default animation duration. @@ -304,7 +348,7 @@ public void onAnimationEnd(Animator animation) { } }); - flutterView.removeFirstFrameListener(this); + flutterEngine.getRenderer().removeOnFirstFrameRenderedListener(this); } }); @@ -373,14 +417,15 @@ public FlutterView getFlutterView() { * Reloading/restarting Dart within a given FlutterView is not supported. */ private void doInitialFlutterViewRun() { - if (BuildConfig.DEBUG && flutterView.getFlutterNativeView().isApplicationRunning()) { +// if (BuildConfig.DEBUG && flutterView.getFlutterNativeView().isApplicationRunning()) { + if (flutterEngine.isApplicationRunning()) { throw new RuntimeException("Tried to initialize Dart execution in Flutter engine that is already running."); } if (getInitialRoute() != null) { - flutterView.setInitialRoute(getInitialRoute()); + setInitialRoute(getInitialRoute()); } - flutterView.runFromBundle(getAppBundlePath(), null, "main", false); + runFromBundle(getAppBundlePath(), null, "main", false); } @Nullable @@ -421,8 +466,72 @@ protected boolean retainFlutterIsolateAfterFragmentDestruction() { @NonNull private Context getContextCompat() { - return Build.VERSION.SDK_INT >= 23 - ? getContext() - : getActivity(); + return getActivity(); + // TODO(mattcarroll): bring back when target SDK is rev'd +// return Build.VERSION.SDK_INT >= 23 +// ? getContext() +// : getActivity(); + } + + public void setInitialRoute(String route) { + mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); + } + + public void pushRoute(String route) { + mFlutterNavigationChannel.invokeMethod("pushRoute", route); + } + + public void popRoute() { + mFlutterNavigationChannel.invokeMethod("popRoute", null); + } + + //------ START RUN FROM BUNDLE ----- + public void runFromBundle(FlutterRunArguments args) { + assertFlutterEngineAttached(); + // TODO(mattcarroll): why do we need to resetAccessibilityTree here? Can we call that from within FlutterView somewhere? + flutterView.resetAccessibilityTree(); + flutterEngine.runFromBundle(args); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath) { + runFromBundle(bundlePath, defaultPath, "main", false); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { + runFromBundle(bundlePath, defaultPath, entrypoint, false); + } + + /** + * @deprecated + * Please use runFromBundle with `FlutterRunArguments`. + * Parameter `reuseRuntimeController` has no effect. + */ + @Deprecated + public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { + FlutterRunArguments args = new FlutterRunArguments(); + args.bundlePath = bundlePath; + args.entrypoint = entrypoint; + args.defaultPath = defaultPath; + runFromBundle(args); + } + + private boolean isFlutterEngineAttached() { + return flutterEngine != null && flutterEngine.isAttached(); + } + + void assertFlutterEngineAttached() { + if (!isFlutterEngineAttached()) + throw new AssertionError("Platform view is not attached"); } + //------ END RUN FROM BUNDLE ---- } diff --git a/shell/platform/android/io/flutter/embedding/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/FlutterJNI.java new file mode 100644 index 0000000000000..62aea0e729c65 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/FlutterJNI.java @@ -0,0 +1,106 @@ +package io.flutter.embedding; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.view.Surface; + +import java.nio.ByteBuffer; + +public class FlutterJNI { + //----- Start from FlutterView ----- + public native void nativeSurfaceCreated(long nativePlatformViewAndroid, + Surface surface, + int backgroundColor); + + public native void nativeSurfaceChanged(long nativePlatformViewAndroid, + int width, + int height); + + public native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); + + public native void nativeSetViewportMetrics(long nativePlatformViewAndroid, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft); + + public native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); + + public native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, + ByteBuffer buffer, + int position); + + public native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, + int id, + int action, + ByteBuffer args, + int argsPosition); + + public native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); + + public native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); + + public native boolean nativeGetIsSoftwareRenderingEnabled(); + + public native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, SurfaceTexture surfaceTexture); + + public native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); + + public native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); + //------- End from FlutterView ----- + + //------ Start from FlutterNativeView ---- + public native long nativeAttach(FlutterEngine engine, boolean isBackgroundView); + public native void nativeDestroy(long nativePlatformViewAndroid); + public native void nativeDetach(long nativePlatformViewAndroid); + + public native void nativeRunBundleAndSnapshotFromLibrary( + long nativePlatformViewAndroid, + String bundlePath, + String defaultPath, + String entrypoint, + String libraryUrl, + AssetManager manager + ); + + public native String nativeGetObservatoryUri(); + + // Send an empty platform message to Dart. + public native void nativeDispatchEmptyPlatformMessage( + long nativePlatformViewAndroid, + String channel, + int responseId + ); + + // Send a data-carrying platform message to Dart. + public native void nativeDispatchPlatformMessage( + long nativePlatformViewAndroid, + String channel, + ByteBuffer message, + int position, + int responseId + ); + + // Send an empty response to a platform message received from Dart. + public native void nativeInvokePlatformMessageEmptyResponseCallback( + long nativePlatformViewAndroid, + int responseId + ); + + // Send a data-carrying response to a platform message received from Dart. + public native void nativeInvokePlatformMessageResponseCallback( + long nativePlatformViewAndroid, + int responseId, + ByteBuffer message, + int position + ); + //------ End from FlutterNativeView ---- +} diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java index 40fbfee8a4bd0..259308eabd7a8 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -1,23 +1,51 @@ package io.flutter.embedding; import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; import android.os.Build; import android.support.annotation.NonNull; +import android.view.Surface; +import java.nio.ByteBuffer; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicLong; + +import io.flutter.view.TextureRegistry; @TargetApi(Build.VERSION_CODES.JELLY_BEAN) -public class FlutterRenderer { +public class FlutterRenderer implements TextureRegistry { private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); + private final FlutterEngine flutterEngine; + private final FlutterJNI flutterJNI; + private final long nativeObjectReference; + private RenderSurface renderSurface; + + FlutterRenderer( + @NonNull FlutterEngine flutterEngine, + @NonNull FlutterJNI flutterJNI, + long nativeObjectReference) { + this.flutterEngine = flutterEngine; + this.flutterJNI = flutterJNI; + this.nativeObjectReference = nativeObjectReference; + } + + public void attachToRenderSurface(@NonNull RenderSurface renderSurface) { + // TODO(mattcarroll): what is our desired behavior when attaching to an already attached renderer? + if (this.renderSurface != null) { + detachFromRenderSurface(); + } - public void attachToView() { - // TODO(mattcarroll): do whatever is needed to start rendering and processing user input + this.renderSurface = renderSurface; } - public void detachFromView() { - // TODO(mattcarroll): stop rendering to surface and stop processing user input + public void detachFromRenderSurface() { + // TODO(mattcarroll): do we care if we're asked to detach without first being attached? + if (this.renderSurface != null) { + this.renderSurface = null; + } } public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { @@ -34,7 +62,177 @@ private void notifyFirstFrameListeners() { } } + //------ START TextureRegistry IMPLEMENTATION ----- + private final AtomicLong nextTextureId = new AtomicLong(0L); + + // TODO(mattcarroll): detachFromGLContext requires API 16. what should we do for earlier APIs? + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public SurfaceTextureEntry createSurfaceTexture() { + final SurfaceTexture surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry( + nextTextureId.getAndIncrement(), + surfaceTexture + ); + registerTexture(entry.id(), surfaceTexture); + 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; + this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture texture) { + markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id); + } + }); + } + + @Override + public SurfaceTexture surfaceTexture() { + return surfaceTexture; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + released = true; + unregisterTexture(id); + surfaceTexture.release(); + } + } + //------ END TextureRegistry IMPLEMENTATION ---- + + //------ START MIGRATION FROM FlutterEngine to FlutterRenderer ----- + public void surfaceCreated(Surface surface, int backgroundColor) { + flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface, backgroundColor); + } + + public void surfaceChanged(int width, int height) { + flutterJNI.nativeSurfaceChanged(nativeObjectReference, width, height); + } + + public void surfaceDestroyed() { + flutterJNI.nativeSurfaceDestroyed(nativeObjectReference); + } + + public void setViewportMetrics(float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft) { + flutterJNI.nativeSetViewportMetrics( + nativeObjectReference, + devicePixelRatio, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft + ); + } + + public Bitmap getBitmap() { + return flutterJNI.nativeGetBitmap(nativeObjectReference); + } + + public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { + flutterJNI.nativeDispatchPointerDataPacket(nativeObjectReference, buffer, position); + } + + public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + flutterJNI.nativeRegisterTexture(nativeObjectReference, textureId, surfaceTexture); + } + + public void markTextureFrameAvailable(long textureId) { + flutterJNI.nativeMarkTextureFrameAvailable(nativeObjectReference, textureId); + } + + public void unregisterTexture(long textureId) { + flutterJNI.nativeUnregisterTexture(nativeObjectReference, textureId); + } + + public boolean isSoftwareRenderingEnabled() { + return flutterJNI.nativeGetIsSoftwareRenderingEnabled(); + } + + public void setAccessibilityFeatures(int flags) { + flutterJNI.nativeSetAccessibilityFeatures(nativeObjectReference, flags); + } + + public void setSemanticsEnabled(boolean enabled) { + flutterJNI.nativeSetSemanticsEnabled(nativeObjectReference, enabled); + } + + public void dispatchSemanticsAction(int id, + int action, + ByteBuffer args, + int argsPosition) { + flutterJNI.nativeDispatchSemanticsAction( + nativeObjectReference, + id, + action, + args, + argsPosition + ); + } + //------ END MIGRATION FROM FlutterEngine to FlutterRenderer ---- + + //------ START PACKAGE PRIVATE MESSAGES FROM FlutterEngine ------ + void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + if (renderSurface != null) { + renderSurface.updateCustomAccessibilityActions(buffer, strings); + } + } + + void updateSemantics(ByteBuffer buffer, String[] strings) { + if (renderSurface != null) { + renderSurface.updateSemantics(buffer, strings); + } + } + + void onFirstFrameRendered() { + if (renderSurface != null) { + renderSurface.onFirstFrameRendered(); + notifyFirstFrameListeners(); + } + } + //------ END PACKAGE PRIVATE MESSAGES FROM FlutterEngine ------ + public interface OnFirstFrameRenderedListener { void onFirstFrameRendered(); } + + public interface RenderSurface { + void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings); + + void updateSemantics(ByteBuffer buffer, String[] strings); + + void onFirstFrameRendered(); + } } diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index 5ee2a400a3e19..90d9bcb318868 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -6,54 +6,57 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; -import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Rect; -import android.graphics.SurfaceTexture; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.provider.Settings; +import android.support.annotation.NonNull; import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; -import android.view.*; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.WindowInsets; +import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import io.flutter.embedding.legacy.AccessibilityBridge; -import io.flutter.embedding.legacy.FlutterNativeView; -import io.flutter.embedding.legacy.FlutterPluginRegistry; -import io.flutter.embedding.legacy.TextInputPlugin; -import io.flutter.plugin.common.*; -import io.flutter.plugin.platform.PlatformPlugin; -import io.flutter.view.FlutterRunArguments; -import io.flutter.view.TextureRegistry; -import io.flutter.view.VsyncWaiter; + import org.json.JSONException; -import org.json.JSONObject; -import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import io.flutter.embedding.legacy.AccessibilityBridge; +import io.flutter.embedding.legacy.TextInputPlugin; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.JSONMessageCodec; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.view.VsyncWaiter; /** * An Android view containing a Flutter app. */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class FlutterView extends SurfaceView implements - TextureRegistry, + FlutterRenderer.RenderSurface, AccessibilityManager.AccessibilityStateChangeListener { private static final String TAG = "FlutterView"; @@ -89,150 +92,129 @@ static final class ViewportMetrics { } private final InputMethodManager mImm; - private final TextInputPlugin mTextInputPlugin; - private final SurfaceHolder.Callback mSurfaceCallback; private final ViewportMetrics mMetrics; private final AccessibilityManager mAccessibilityManager; - private final MethodChannel mFlutterLocalizationChannel; - private final MethodChannel mFlutterNavigationChannel; - private final BasicMessageChannel mFlutterKeyEventChannel; - private final BasicMessageChannel mFlutterLifecycleChannel; - private final BasicMessageChannel mFlutterSystemChannel; - private final BasicMessageChannel mFlutterSettingsChannel; - private final List mActivityLifecycleListeners; - private final List mFirstFrameListeners; - private final AtomicLong nextTextureId = new AtomicLong(0L); - private FlutterNativeView mNativeView; - private FlutterEngine flutterEngine; private final AnimationScaleObserver mAnimationScaleObserver; - private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not + + private FlutterRenderer flutterRenderer; + private BinaryMessenger pluginMessenger; + + private TextInputPlugin mTextInputPlugin; + private MethodChannel mFlutterLocalizationChannel; + private BasicMessageChannel mFlutterKeyEventChannel; + private BasicMessageChannel mFlutterSettingsChannel; private InputConnection mLastInputConnection; + private boolean isAttachedToRenderer = false; + private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not + private int backgroundColor; + // Accessibility private boolean mAccessibilityEnabled = false; private boolean mTouchExplorationEnabled = false; private int mAccessibilityFeatureFlags = 0; + private AccessibilityBridge mAccessibilityNodeProvider; private TouchExplorationListener mTouchExplorationListener; + private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + assertAttachedToFlutterRenderer(); + flutterRenderer.surfaceCreated(holder.getSurface(), backgroundColor); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + assertAttachedToFlutterRenderer(); + flutterRenderer.surfaceChanged(width, height); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + assertAttachedToFlutterRenderer(); + flutterRenderer.surfaceDestroyed(); + } + }; + //------ START VIEW OVERRIDES ----- public FlutterView(Context context) { this(context, null); } public FlutterView(Context context, AttributeSet attrs) { - this(context, attrs, null, null); - } - - public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView, FlutterEngine flutterEngine) { super(context, attrs); - Activity activity = (Activity) getContext(); - if (nativeView == null) { - mNativeView = new FlutterNativeView(activity.getApplicationContext()); - this.flutterEngine = new FlutterEngine(mNativeView, new FlutterPluginRegistry(mNativeView, getContext())); - } else { - mNativeView = nativeView; - this.flutterEngine = flutterEngine; - } - mNativeView.attachViewAndActivity(this, activity); - - mIsSoftwareRenderingEnabled = flutterEngine.nativeGetIsSoftwareRenderingEnabled(); - mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); + // Cache references to Objects used throughout FlutterView. mMetrics = new ViewportMetrics(); mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); + + // Process any theme and attribute preferences. + readBackgroundColorFromThemeAndAttributes(); + + // Initialize this View as needed. setFocusable(true); setFocusableInTouchMode(true); + } + // TODO(mattcarroll): add XML attribute support for background color + private void readBackgroundColorFromThemeAndAttributes() { int color = 0xFF000000; TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true); + getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true); if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) { color = typedValue.data; } // TODO(abarth): Consider letting the developer override this color. - final int backgroundColor = color; - - mSurfaceCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceCreated(SurfaceHolder holder) { - assertFlutterEngineAttached(); - flutterEngine.nativeSurfaceCreated(mNativeView.get(), holder.getSurface(), backgroundColor); - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - assertFlutterEngineAttached(); - flutterEngine.nativeSurfaceChanged(mNativeView.get(), width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - assertFlutterEngineAttached(); - flutterEngine.nativeSurfaceDestroyed(mNativeView.get()); - } - }; - getHolder().addCallback(mSurfaceCallback); - - mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - - mActivityLifecycleListeners = new ArrayList<>(); - mFirstFrameListeners = new ArrayList<>(); - - // Configure the platform plugins and flutter channels. - mFlutterLocalizationChannel = new MethodChannel(flutterEngine, "flutter/localization", JSONMethodCodec.INSTANCE); - mFlutterNavigationChannel = new MethodChannel(flutterEngine, "flutter/navigation", JSONMethodCodec.INSTANCE); - mFlutterKeyEventChannel = new BasicMessageChannel<>(flutterEngine, "flutter/keyevent", JSONMessageCodec.INSTANCE); - mFlutterLifecycleChannel = new BasicMessageChannel<>(flutterEngine, "flutter/lifecycle", StringCodec.INSTANCE); - mFlutterSystemChannel = new BasicMessageChannel<>(flutterEngine, "flutter/system", JSONMessageCodec.INSTANCE); - mFlutterSettingsChannel = new BasicMessageChannel<>(flutterEngine, "flutter/settings", JSONMessageCodec.INSTANCE); - - PlatformPlugin platformPlugin = new PlatformPlugin(activity); - MethodChannel flutterPlatformChannel = new MethodChannel(flutterEngine, "flutter/platform", JSONMethodCodec.INSTANCE); - flutterPlatformChannel.setMethodCallHandler(platformPlugin); - addActivityLifecycleListener(platformPlugin); - mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - mTextInputPlugin = new TextInputPlugin(this); - - setLocale(getResources().getConfiguration().locale); - setUserSettings(); - } - - public void addActivityLifecycleListener(ActivityLifecycleListener listener) { - mActivityLifecycleListeners.add(listener); + backgroundColor = color; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + Log.d(TAG, "onAttachedToWindow()"); + + // Read accessibility settings. mAccessibilityEnabled = mAccessibilityManager.isEnabled(); mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); - getContext().getContentResolver().registerContentObserver(transitionUri, false, mAnimationScaleObserver); - } - if (mAccessibilityEnabled || mTouchExplorationEnabled) { ensureAccessibilityEnabled(); } if (mTouchExplorationEnabled) { mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; } + // Apply additional accessibility settings updateAccessibilityFeatures(); resetWillNotDraw(); mAccessibilityManager.addAccessibilityStateChangeListener(this); + + // Start listening for changes to accessibility settings. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (mTouchExplorationListener == null) { mTouchExplorationListener = new TouchExplorationListener(); } mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener); } + + // Start listening for changes to Android's animation scale setting. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); + getContext().getContentResolver().registerContentObserver(transitionUri, false, mAnimationScaleObserver); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + Log.d(TAG, "onDetachedFromWindow()"); + + // Stop listening for changes to Android's animation scale setting. getContext().getContentResolver().unregisterContentObserver(mAnimationScaleObserver); + + // Stop listening for changes to accessibility settings. mAccessibilityManager.removeAccessibilityStateChangeListener(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener); @@ -253,24 +235,13 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - setLocale(newConfig.locale); - setUserSettings(); - } - - private void setLocale(Locale locale) { - mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry())); - } - - private void setUserSettings() { - Map message = new HashMap<>(); - message.put("textScaleFactor", getResources().getConfiguration().fontScale); - message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext())); - mFlutterSettingsChannel.send(message); + setFlutterLocale(newConfig.locale); + setFlutterUserSettings(); } @Override public boolean onTouchEvent(MotionEvent event) { - if (!isFlutterEngineAttached()) { + if (!isAttachedToRenderer) { return false; } @@ -308,7 +279,7 @@ public boolean onTouchEvent(MotionEvent event) { } assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - flutterEngine.nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + flutterRenderer.dispatchPointerDataPacket(packet, packet.position()); return true; } @@ -411,7 +382,7 @@ private int getPointerDeviceTypeForToolType(int toolType) { @Override public boolean onHoverEvent(MotionEvent event) { - if (!isFlutterEngineAttached()) { + if (!isAttachedToRenderer) { return false; } @@ -423,6 +394,21 @@ public boolean onHoverEvent(MotionEvent event) { return handled; } + private boolean handleAccessibilityHoverEvent(MotionEvent event) { + if (!mTouchExplorationEnabled) { + return false; + } + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { + mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY()); + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + mAccessibilityNodeProvider.handleTouchExplorationExit(); + } else { + Log.d("flutter", "unexpected accessibility hover event: " + event); + return false; + } + return true; + } + @Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { mMetrics.physicalWidth = width; @@ -473,11 +459,10 @@ protected boolean fitSystemWindows(Rect insets) { } private void updateViewportMetrics() { - if (!isFlutterEngineAttached()) + if (!isAttachedToRenderer) return; - flutterEngine.nativeSetViewportMetrics( - mNativeView.get(), + flutterRenderer.setViewportMetrics( mMetrics.devicePixelRatio, mMetrics.physicalWidth, mMetrics.physicalHeight, @@ -496,60 +481,9 @@ private void updateViewportMetrics() { VsyncWaiter.refreshPeriodNanos = (long) (1000000000.0 / fps); } - // TODO(mattcarroll): detachFromGLContext requires API 16. what should we do for earlier APIs? - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - @Override - public SurfaceTextureEntry createSurfaceTexture() { - final SurfaceTexture surfaceTexture = new SurfaceTexture(0); - surfaceTexture.detachFromGLContext(); - final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), - surfaceTexture); - flutterEngine.nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); - 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; - this.surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture texture) { - flutterEngine.nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); - } - }); - } - - @Override - public SurfaceTexture surfaceTexture() { - return surfaceTexture; - } - - @Override - public long id() { - return id; - } - - @Override - public void release() { - if (released) { - return; - } - released = true; - flutterEngine.nativeUnregisterTexture(mNativeView.get(), id); - surfaceTexture.release(); - } - } - //------ END VIEW OVERRIDES ---- - - //----- START KEYEVENT.CALLBACK ----- @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (!isFlutterEngineAttached()) { + if (!isAttachedToRenderer) { return super.onKeyUp(keyCode, event); } @@ -563,7 +497,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - if (!isFlutterEngineAttached()) { + if (!isAttachedToRenderer) { return super.onKeyDown(keyCode, event); } @@ -588,82 +522,41 @@ private void encodeKeyEvent(KeyEvent event, Map message) { message.put("scanCode", event.getScanCode()); message.put("metaState", event.getMetaState()); } - //----- END KEYEVENT.CALLBACK ----- - - public FlutterEngine getFlutterEngine() { - return flutterEngine; - } - - //----- START METHODS INVOKED BY FRAGMENT ----- - public FlutterNativeView getFlutterNativeView() { - return mNativeView; - } - - public FlutterPluginRegistry getPluginRegistry() { - return mNativeView.getPluginRegistry(); - } - //----- END METHODS INVOKED BY FRAGMENT --- - - //------ START CALLS FORWARDED FROM FRAGMENT ---- - public void onStart() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); - } - - public void onPause() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); - } + //------ END VIEW OVERRIDES ---- - public void onPostResume() { - updateAccessibilityFeatures(); - for (ActivityLifecycleListener listener : mActivityLifecycleListeners) { - listener.onPostResume(); + //----- START AccessibilityStateChangeListener ----- + @Override + public void onAccessibilityStateChanged(boolean enabled) { + if (enabled) { + ensureAccessibilityEnabled(); + } else { + mAccessibilityEnabled = false; + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.setAccessibilityEnabled(false); + } + flutterRenderer.setSemanticsEnabled(false); } - mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); - } - - public void onStop() { - mFlutterLifecycleChannel.send("AppLifecycleState.paused"); - } - - public FlutterNativeView detach() { - if (!isFlutterEngineAttached()) - return null; - getHolder().removeCallback(mSurfaceCallback); - mNativeView.detach(); - - FlutterNativeView view = mNativeView; - mNativeView = null; - return view; - } - - public void destroy() { - if (!isFlutterEngineAttached()) - return; - - getHolder().removeCallback(mSurfaceCallback); - - mNativeView.destroy(); - mNativeView = null; - } - - public void onMemoryPressure() { - Map message = new HashMap<>(1); - message.put("type", "memoryPressure"); - mFlutterSystemChannel.send(message); - } - - public void setInitialRoute(String route) { - mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); + resetWillNotDraw(); } - public void pushRoute(String route) { - mFlutterNavigationChannel.invokeMethod("pushRoute", route); + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + if (mAccessibilityEnabled) + return mAccessibilityNodeProvider; + // TODO(goderbauer): when a11y is off this should return a one-off snapshot of + // the a11y + // tree. + return null; } - public void popRoute() { - mFlutterNavigationChannel.invokeMethod("popRoute", null); + private void resetWillNotDraw() { + if (!mIsSoftwareRenderingEnabled) { + setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled)); + } else { + setWillNotDraw(false); + } } - //------ END CALLS FORWARDED FROM FRAGMENT ---- + //----- END AccessibilityStateChangeListener ---- //------- START ACCESSIBILITY ------ // Called by native to update the semantics/accessibility tree. @@ -695,7 +588,7 @@ public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action) { } public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, Object args) { - if (!isFlutterEngineAttached()) + if (!isAttachedToRenderer) return; ByteBuffer encodedArgs = null; int position = 0; @@ -703,10 +596,10 @@ public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, O encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); position = encodedArgs.position(); } - flutterEngine.nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); + flutterRenderer.dispatchSemanticsAction(id, action.value, encodedArgs, position); } - private void updateAccessibilityFeatures() { + public void updateAccessibilityFeatures() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { String transitionAnimationScale = Settings.Global.getString(getContext().getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE); @@ -716,30 +609,99 @@ private void updateAccessibilityFeatures() { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } } - flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterRenderer.setAccessibilityFeatures(mAccessibilityFeatureFlags); } - private void resetWillNotDraw() { - if (!mIsSoftwareRenderingEnabled) { - setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled)); - } else { - setWillNotDraw(false); + void ensureAccessibilityEnabled() { + if (!isAttachedToRenderer) + return; + mAccessibilityEnabled = true; + if (mAccessibilityNodeProvider == null) { + mAccessibilityNodeProvider = new AccessibilityBridge(this, pluginMessenger); } + flutterRenderer.setSemanticsEnabled(true); + mAccessibilityNodeProvider.setAccessibilityEnabled(true); } - @Override - public void onAccessibilityStateChanged(boolean enabled) { - if (enabled) { - ensureAccessibilityEnabled(); - } else { - mAccessibilityEnabled = false; - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setAccessibilityEnabled(false); - } - flutterEngine.nativeSetSemanticsEnabled(mNativeView.get(), false); + void resetAccessibilityTree() { + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.reset(); } - resetWillNotDraw(); } + //------- END ACCESSIBILITY ---- + + //----- START FLUTTER INTEGRATION ----- + public void attachToFlutterRenderer(@NonNull FlutterRenderer flutterRenderer, @NonNull BinaryMessenger pluginMessenger) { + if (isAttachedToRenderer) { + detachFromFlutterRenderer(); + } + + // TODO(mattcarroll): what should we do if we're not currently attached to the window? + this.flutterRenderer = flutterRenderer; + this.pluginMessenger = pluginMessenger; + + this.flutterRenderer.attachToRenderSurface(this); + + mIsSoftwareRenderingEnabled = flutterRenderer.isSoftwareRenderingEnabled(); + + // Configure the platform plugins and flutter channels. + mFlutterLocalizationChannel = new MethodChannel(pluginMessenger, "flutter/localization", JSONMethodCodec.INSTANCE); + mFlutterKeyEventChannel = new BasicMessageChannel<>(pluginMessenger, "flutter/keyevent", JSONMessageCodec.INSTANCE); + mFlutterSettingsChannel = new BasicMessageChannel<>(pluginMessenger, "flutter/settings", JSONMessageCodec.INSTANCE); + mTextInputPlugin = new TextInputPlugin(this, pluginMessenger); + + setFlutterLocale(getResources().getConfiguration().locale); + setFlutterUserSettings(); + + getHolder().addCallback(mSurfaceCallback); + + isAttachedToRenderer = true; + } + + private void assertAttachedToFlutterRenderer() { + if (!isAttachedToRenderer) + throw new AssertionError("FlutterView is not attached to a FlutterRenderer."); + } + + public void detachFromFlutterRenderer() { + if (!isAttachedToRenderer) + return; + + getHolder().removeCallback(mSurfaceCallback); + + flutterRenderer.detachFromRenderSurface(); + + isAttachedToRenderer = false; + } + + /** + * Send this Android device's {@link Locale} configuration to Flutter. + * @param locale the user's locale + */ + private void setFlutterLocale(@NonNull Locale locale) { + mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry())); + } + + /** + * Send various user preferences of this Android device to Flutter. + * + * For example, sends the user's "text scale factor" preferences, as well as the user's clock + * format preference. + */ + private void setFlutterUserSettings() { + Map message = new HashMap<>(); + message.put("textScaleFactor", getResources().getConfiguration().fontScale); + message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext())); + mFlutterSettingsChannel.send(message); + } + //----- END FLUTTER INTEGRATION ----- + + //------ START RenderingSurface ----- + @Override + public void onFirstFrameRendered() { + // no-op + } + //------ END RenderingSurface ---- /// Must match the enum defined in window.dart. private enum AccessibilityFeature { @@ -757,7 +719,7 @@ private enum AccessibilityFeature { // Listens to the global TRANSITION_ANIMATION_SCALE property and notifies us so // that we can disable animations in Flutter. private class AnimationScaleObserver extends ContentObserver { - public AnimationScaleObserver(Handler handler) { + AnimationScaleObserver(Handler handler) { super(handler); } @@ -772,12 +734,12 @@ public void onChange(boolean selfChange) { public void onChange(boolean selfChange, Uri uri) { String value = Settings.Global.getString(getContext().getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE); - if (value == "0") { + if (value.equals("0")) { mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; } else { mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; } - flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterRenderer.setAccessibilityFeatures(mAccessibilityFeatureFlags); } } @@ -790,153 +752,16 @@ public void onTouchExplorationStateChanged(boolean enabled) { mTouchExplorationEnabled = true; ensureAccessibilityEnabled(); mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterRenderer.setAccessibilityFeatures(mAccessibilityFeatureFlags); } else { mTouchExplorationEnabled = false; if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.handleTouchExplorationExit(); } mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - flutterEngine.nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + flutterRenderer.setAccessibilityFeatures(mAccessibilityFeatureFlags); } resetWillNotDraw(); } } - - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider() { - if (mAccessibilityEnabled) - return mAccessibilityNodeProvider; - // TODO(goderbauer): when a11y is off this should return a one-off snapshot of - // the a11y - // tree. - return null; - } - - private AccessibilityBridge mAccessibilityNodeProvider; - - void ensureAccessibilityEnabled() { - if (!isFlutterEngineAttached()) - return; - mAccessibilityEnabled = true; - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = new AccessibilityBridge(this); - } - flutterEngine.nativeSetSemanticsEnabled(mNativeView.get(), true); - mAccessibilityNodeProvider.setAccessibilityEnabled(true); - } - - void resetAccessibilityTree() { - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.reset(); - } - } - - private boolean handleAccessibilityHoverEvent(MotionEvent event) { - if (!mTouchExplorationEnabled) { - return false; - } - if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { - mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY()); - } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { - mAccessibilityNodeProvider.handleTouchExplorationExit(); - } else { - Log.d("flutter", "unexpected accessibility hover event: " + event); - return false; - } - return true; - } - //------- END ACCESSIBILITY ---- - - //------ START RUN FROM BUNDLE ----- - public void runFromBundle(FlutterRunArguments args) { - assertFlutterEngineAttached(); - onFlutterEngineWillStart(); - mNativeView.runFromBundle(args); - onFlutterEngineDidStart(); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath) { - runFromBundle(bundlePath, defaultPath, "main", false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { - runFromBundle(bundlePath, defaultPath, entrypoint, false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = bundlePath; - args.entrypoint = entrypoint; - args.defaultPath = defaultPath; - runFromBundle(args); - } - - private boolean isFlutterEngineAttached() { - return mNativeView != null && mNativeView.isAttached(); - } - - void assertFlutterEngineAttached() { - if (!isFlutterEngineAttached()) - throw new AssertionError("Platform view is not attached"); - } - - private void onFlutterEngineWillStart() { - resetAccessibilityTree(); - } - - private void onFlutterEngineDidStart() { - } - //------ END RUN FROM BUNDLE ---- - - //------ START ENGINE INTERACTIONS ----- - // Called by native to notify first Flutter frame rendered. - @SuppressWarnings("unused") - public void onFirstFrame() { - // Allow listeners to remove themselves when they are called. - List listeners = new ArrayList<>(mFirstFrameListeners); - for (FirstFrameListener listener : listeners) { - listener.onFirstFrame(); - } - } - - /** - * Provide a listener that will be called once when the FlutterView renders its - * first frame to the underlaying SurfaceView. - */ - public void addFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.add(listener); - } - - /** - * Remove an existing first frame listener. - */ - public void removeFirstFrameListener(FirstFrameListener listener) { - mFirstFrameListeners.remove(listener); - } - - /** - * Listener will be called on the Android UI thread once when Flutter renders - * the first frame. - */ - public interface FirstFrameListener { - void onFirstFrame(); - } - //------ END ENGINE INTERACTIONS ---- } diff --git a/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java b/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java index 94de6dca1ae32..7ff2e09176836 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/embedding/legacy/AccessibilityBridge.java @@ -15,7 +15,10 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; + +import io.flutter.embedding.FlutterEngine; import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.embedding.FlutterView; @@ -108,14 +111,14 @@ enum Flag { final int value; } - public AccessibilityBridge(FlutterView owner) { + public AccessibilityBridge(FlutterView owner, BinaryMessenger pluginMessenger) { assert owner != null; mOwner = owner; mObjects = new HashMap(); mCustomAccessibilityActions = new HashMap(); previousRoutes = new ArrayList<>(); mFlutterAccessibilityChannel = new BasicMessageChannel<>( - owner, "flutter/accessibility", StandardMessageCodec.INSTANCE); + pluginMessenger, "flutter/accessibility", StandardMessageCodec.INSTANCE); mDecorView = ((Activity) owner.getContext()).getWindow().getDecorView(); } diff --git a/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java b/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java deleted file mode 100644 index c5b32c39a1e60..0000000000000 --- a/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.embedding.legacy; - -import android.app.Activity; -import android.content.Context; -import android.content.res.AssetManager; -import android.util.Log; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.view.FlutterRunArguments; -import io.flutter.embedding.FlutterView; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -public class FlutterNativeView implements BinaryMessenger { - private static final String TAG = "FlutterNativeView"; - - private final Map mMessageHandlers; - private int mNextReplyId = 1; - private final Map mPendingReplies = new HashMap<>(); - - private final FlutterPluginRegistry mPluginRegistry; - private long mNativePlatformView; - private FlutterView mFlutterView; - private final Context mContext; - private boolean applicationIsRunning; - - public FlutterNativeView(Context context) { - this(context, false); - } - - public FlutterNativeView(Context context, boolean isBackgroundView) { - mContext = context; - mPluginRegistry = new FlutterPluginRegistry(this, context); - attach(this, isBackgroundView); - assertAttached(); - mMessageHandlers = new HashMap<>(); - } - - public void detach() { - mPluginRegistry.detach(); - mFlutterView = null; - nativeDetach(mNativePlatformView); - } - - public void destroy() { - mPluginRegistry.destroy(); - mFlutterView = null; - nativeDestroy(mNativePlatformView); - mNativePlatformView = 0; - applicationIsRunning = false; - } - - public FlutterPluginRegistry getPluginRegistry() { - return mPluginRegistry; - } - - public void attachViewAndActivity(FlutterView flutterView, Activity activity) { - mFlutterView = flutterView; - mPluginRegistry.attach(flutterView, activity); - } - - public boolean isAttached() { - return mNativePlatformView != 0; - } - - public long get() { - return mNativePlatformView; - } - - public void assertAttached() { - if (!isAttached()) throw new AssertionError("Platform view is not attached"); - } - - public void runFromBundle(FlutterRunArguments args) { - if (args.bundlePath == null) { - throw new AssertionError("A bundlePath must be specified"); - } else if (args.entrypoint == null) { - throw new AssertionError("An entrypoint must be specified"); - } - runFromBundleInternal(args.bundlePath, args.entrypoint, args.libraryPath, args.defaultPath); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, - boolean reuseRuntimeController) { - runFromBundleInternal(bundlePath, entrypoint, null, defaultPath); - } - - private void runFromBundleInternal(String bundlePath, String entrypoint, - String libraryPath, String defaultPath) { - assertAttached(); - if (applicationIsRunning) - throw new AssertionError( - "This Flutter engine instance is already running an application"); - nativeRunBundleAndSnapshotFromLibrary(mNativePlatformView, bundlePath, - defaultPath, entrypoint, libraryPath, mContext.getResources().getAssets()); - - applicationIsRunning = true; - } - - public boolean isApplicationRunning() { - return applicationIsRunning; - } - - public static String getObservatoryUri() { - return nativeGetObservatoryUri(); - } - - @Override - public void send(String channel, ByteBuffer message) { - send(channel, message, null); - } - - @Override - public void send(String channel, ByteBuffer message, BinaryReply callback) { - if (!isAttached()) { - Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel); - return; - } - - int replyId = 0; - if (callback != null) { - replyId = mNextReplyId++; - mPendingReplies.put(replyId, callback); - } - if (message == null) { - nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId); - } else { - nativeDispatchPlatformMessage( - mNativePlatformView, channel, message, message.position(), replyId); - } - } - - @Override - public void setMessageHandler(String channel, BinaryMessageHandler handler) { - if (handler == null) { - mMessageHandlers.remove(channel); - } else { - mMessageHandlers.put(channel, handler); - } - } - - private void attach(FlutterNativeView view, boolean isBackgroundView) { - mNativePlatformView = nativeAttach(view, isBackgroundView); - } - - // Called by native to send us a platform message. - private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { - assertAttached(); - BinaryMessageHandler handler = mMessageHandlers.get(channel); - if (handler != null) { - try { - final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); - handler.onMessage(buffer, new BinaryReply() { - private final AtomicBoolean done = new AtomicBoolean(false); - @Override - public void reply(ByteBuffer reply) { - if (!isAttached()) { - Log.d(TAG, - "handlePlatformMessage replying to a detached view, channel=" - + channel); - return; - } - if (done.getAndSet(true)) { - throw new IllegalStateException("Reply already submitted"); - } - if (reply == null) { - nativeInvokePlatformMessageEmptyResponseCallback( - mNativePlatformView, replyId); - } else { - nativeInvokePlatformMessageResponseCallback( - mNativePlatformView, replyId, reply, reply.position()); - } - } - }); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message listener", ex); - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); - } - return; - } - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); - } - - // Called by native to respond to a platform message that we sent. - private void handlePlatformMessageResponse(int replyId, byte[] reply) { - BinaryReply callback = mPendingReplies.remove(replyId); - if (callback != null) { - try { - callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message reply handler", ex); - } - } - } - - // Called by native to update the semantics/accessibility tree. - private void updateSemantics(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) return; - mFlutterView.updateSemantics(buffer, strings); - } - - // Called by native to update the custom accessibility actions. - private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) - return; - mFlutterView.updateCustomAccessibilityActions(buffer, strings); - } - - // Called by native to notify first Flutter frame rendered. - private void onFirstFrame() { - if (mFlutterView == null) return; - mFlutterView.onFirstFrame(); - } - - // Called by native to notify when the engine is restarted (cold reload). - @SuppressWarnings("unused") - private void onPreEngineRestart() { - if (mPluginRegistry == null) - return; - mPluginRegistry.onPreEngineRestart(); - } - - private static native long nativeAttach(FlutterNativeView view, boolean isBackgroundView); - private static native void nativeDestroy(long nativePlatformViewAndroid); - private static native void nativeDetach(long nativePlatformViewAndroid); - - private static native void nativeRunBundleAndSnapshotFromLibrary( - long nativePlatformViewAndroid, String bundlePath, - String defaultPath, String entrypoint, String libraryUrl, - AssetManager manager); - - private static native String nativeGetObservatoryUri(); - - // Send an empty platform message to Dart. - private static native void nativeDispatchEmptyPlatformMessage( - long nativePlatformViewAndroid, String channel, int responseId); - - // Send a data-carrying platform message to Dart. - private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid, - String channel, ByteBuffer message, int position, int responseId); - - // Send an empty response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageEmptyResponseCallback( - long nativePlatformViewAndroid, int responseId); - - // Send a data-carrying response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageResponseCallback( - long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position); -} diff --git a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java index d6347406a243f..46032e2e023a2 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java @@ -7,9 +7,10 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; + +import io.flutter.embedding.FlutterEngine; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.view.FlutterMain; -import io.flutter.embedding.FlutterView; import io.flutter.view.TextureRegistry; import java.util.ArrayList; @@ -28,8 +29,8 @@ public class FlutterPluginRegistry private Activity mActivity; private Context mAppContext; - private FlutterNativeView mNativeView; - private FlutterView mFlutterView; + private BinaryMessenger mMessenger; + private FlutterEngine flutterEngine; private final PlatformViewsController mPlatformViewsController; private final Map mPluginMap = new LinkedHashMap<>(0); @@ -39,8 +40,8 @@ public class FlutterPluginRegistry private final List mUserLeaveHintListeners = new ArrayList<>(0); private final List mViewDestroyListeners = new ArrayList<>(0); - public FlutterPluginRegistry(FlutterNativeView nativeView, Context context) { - mNativeView = nativeView; + public FlutterPluginRegistry(BinaryMessenger messenger, Context context) { + mMessenger = messenger; mAppContext = context; mPlatformViewsController = new PlatformViewsController(); } @@ -65,16 +66,16 @@ public Registrar registrarFor(String pluginKey) { return new FlutterRegistrar(pluginKey); } - public void attach(FlutterView flutterView, Activity activity) { - mFlutterView = flutterView; + public void attach(FlutterEngine flutterEngine, Activity activity) { + this.flutterEngine = flutterEngine; mActivity = activity; - mPlatformViewsController.attachFlutterView(flutterView); + mPlatformViewsController.attachFlutterEngine(flutterEngine, activity); } public void detach() { - mPlatformViewsController.detachFlutterView(); + mPlatformViewsController.detachFlutterEngine(); mPlatformViewsController.onFlutterViewDestroyed(); - mFlutterView = null; + flutterEngine = null; mActivity = null; } @@ -106,12 +107,12 @@ public Context activeContext() { @Override public BinaryMessenger messenger() { - return mNativeView; + return mMessenger; } @Override public TextureRegistry textures() { - return mFlutterView; + return flutterEngine.getRenderer(); } @Override @@ -119,11 +120,6 @@ public PlatformViewRegistry platformViewRegistry() { return mPlatformViewsController.getRegistry(); } - @Override - public FlutterView view() { - return mFlutterView; - } - @Override public String lookupKeyForAsset(String asset) { return FlutterMain.getLookupKeyForAsset(asset); @@ -228,10 +224,10 @@ public void onUserLeaveHint() { } @Override - public boolean onViewDestroy(FlutterNativeView view) { + public boolean onViewDestroy(FlutterEngine engine) { boolean handled = false; for (ViewDestroyListener listener : mViewDestroyListeners) { - if (listener.onViewDestroy(view)) { + if (listener.onViewDestroy(engine)) { handled = true; } } diff --git a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java index 608687faf2e72..190a17c1d91b5 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java @@ -5,10 +5,13 @@ package io.flutter.embedding.legacy; import android.annotation.TargetApi; +import android.content.Context; import android.os.Build; import android.util.Log; import android.view.MotionEvent; import android.view.View; + +import io.flutter.embedding.FlutterEngine; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.StandardMethodCodec; @@ -40,7 +43,8 @@ public class PlatformViewsController implements MethodChannel.MethodCallHandler private final PlatformViewRegistryImpl mRegistry; - private FlutterView mFlutterView; + private FlutterEngine flutterEngine; + private Context context; private final HashMap vdControllers; @@ -49,20 +53,21 @@ public PlatformViewsController() { vdControllers = new HashMap<>(); } - public void attachFlutterView(FlutterView view) { - if (mFlutterView != null) + public void attachFlutterEngine(FlutterEngine flutterEngine, Context context) { + if (this.flutterEngine != null) throw new AssertionError( - "A PlatformViewsController can only be attached to a single FlutterView.\n" + - "attachFlutterView was called while a FlutterView was already attached." + "A PlatformViewsController can only be attached to a single FlutterEngine.\n" + + "attachFlutterEngine was called while a FlutterView was already attached." ); - mFlutterView = view; - MethodChannel channel = new MethodChannel(view, CHANNEL_NAME, StandardMethodCodec.INSTANCE); + this.flutterEngine = flutterEngine; + this.context = context; + MethodChannel channel = new MethodChannel(flutterEngine, CHANNEL_NAME, StandardMethodCodec.INSTANCE); channel.setMethodCallHandler(this); } - public void detachFlutterView() { - mFlutterView.setMessageHandler(CHANNEL_NAME, null); - mFlutterView = null; + public void detachFlutterEngine() { + flutterEngine.setMessageHandler(CHANNEL_NAME, null); + flutterEngine = null; } public PlatformViewRegistry getRegistry() { @@ -146,9 +151,9 @@ private void createPlatformView(MethodCall call, MethodChannel.Result result) { createParams = viewFactory.getCreateArgsCodec().decodeMessage(ByteBuffer.wrap((byte[]) args.get("params"))); } - TextureRegistry.SurfaceTextureEntry textureEntry = mFlutterView.createSurfaceTexture(); + TextureRegistry.SurfaceTextureEntry textureEntry = flutterEngine.getRenderer().createSurfaceTexture(); VirtualDisplayController vdController = VirtualDisplayController.create( - mFlutterView.getContext(), + context, viewFactory, textureEntry.surfaceTexture(), toPhysicalPixels(logicalWidth), @@ -223,7 +228,7 @@ public void run() { private void onTouch(MethodCall call, MethodChannel.Result result) { List args = call.arguments(); - float density = mFlutterView.getContext().getResources().getDisplayMetrics().density; + float density = context.getResources().getDisplayMetrics().density; int id = (int) args.get(0); Number downTime = (Number) args.get(1); @@ -356,7 +361,7 @@ private static PointerCoords parsePointerCoords(Object rawCoords, float density) } private int toPhysicalPixels(double logicalPixels) { - float density = mFlutterView.getContext().getResources().getDisplayMetrics().density; + float density = context.getResources().getDisplayMetrics().density; return (int) Math.round(logicalPixels * density); } diff --git a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java index d64de9fd89603..23d2617bdf2ad 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java @@ -7,6 +7,8 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; + +import io.flutter.embedding.FlutterEngine; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.embedding.FlutterView; import io.flutter.view.TextureRegistry; @@ -106,13 +108,6 @@ interface Registrar { */ PlatformViewRegistry platformViewRegistry(); - /** - * Returns the {@link FlutterView} that's instantiated by this plugin's - * {@link #activity() activity}. - */ - FlutterView view(); - - /** * Returns the file name for the given asset. * The returned file name can be used to access the asset in the APK @@ -282,7 +277,7 @@ interface UserLeaveHintListener { * adopt the FlutterNativeView by retaining a reference and returning true. */ interface ViewDestroyListener { - boolean onViewDestroy(FlutterNativeView view); + boolean onViewDestroy(FlutterEngine engine); } /** diff --git a/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java b/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java index 8be257bd63113..dd0759fa618d1 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/embedding/legacy/TextInputPlugin.java @@ -14,6 +14,9 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; + +import io.flutter.embedding.FlutterEngine; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.JSONMethodCodec; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -37,11 +40,11 @@ public class TextInputPlugin implements MethodCallHandler { private Editable mEditable; private boolean mRestartInputPending; - public TextInputPlugin(FlutterView view) { + public TextInputPlugin(FlutterView view, BinaryMessenger pluginMessenger) { mView = view; mImm = (InputMethodManager) view.getContext().getSystemService( Context.INPUT_METHOD_SERVICE); - mFlutterChannel = new MethodChannel(view, "flutter/textinput", JSONMethodCodec.INSTANCE); + mFlutterChannel = new MethodChannel(pluginMessenger, "flutter/textinput", JSONMethodCodec.INSTANCE); mFlutterChannel.setMethodCallHandler(this); } diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 315ac0b55bdc0..b1560ab7f6b3f 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -44,11 +44,18 @@ bool CheckException(JNIEnv* env) { } // anonymous namespace -static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = - nullptr; +static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; + +// FlutterView.java, from original embedding API. Not used in 2nd iteration of embedding. static fml::jni::ScopedJavaGlobalRef* g_flutter_view_class = nullptr; -static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = - nullptr; + +// FlutterNativeView.java, from original embedding API. Not used in 2nd iteration of embedding. +static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = nullptr; + +// FlutterJNI.java, used in 2nd iteration of embedding to centralize all JNI calls. +static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; +static fml::jni::ScopedJavaGlobalRef* g_flutter_engine_class = nullptr; + static fml::jni::ScopedJavaGlobalRef* g_surface_texture_class = nullptr; // Called By Native @@ -150,6 +157,7 @@ static jlong Attach(JNIEnv* env, jclass clazz, jobject flutterView, jboolean is_background_view) { + FML_LOG(ERROR) << "This is a test!"; fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterView); auto shell_holder = std::make_unique( FlutterMain::Get().GetSettings(), java_object, is_background_view); @@ -160,14 +168,37 @@ static jlong Attach(JNIEnv* env, } } +static jlong AttachJNI(JNIEnv* env, + jclass clazz, + jobject flutterJNI, + jboolean is_background_view) { + FML_LOG(ERROR) << "Attaching to FlutterJNI"; + fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI); + auto shell_holder = std::make_unique( + FlutterMain::Get().GetSettings(), java_object, is_background_view); + if (shell_holder->IsValid()) { + return reinterpret_cast(shell_holder.release()); + } else { + return 0; + } +} + static void Detach(JNIEnv* env, jobject jcaller, jlong shell_holder) { // Nothing to do. } +static void DetachJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { + // Nothing to do. +} + static void Destroy(JNIEnv* env, jobject jcaller, jlong shell_holder) { delete ANDROID_SHELL_HOLDER; } +static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { + delete ANDROID_SHELL_HOLDER; +} + static jstring GetObservatoryUri(JNIEnv* env, jclass clazz) { return env->NewStringUTF( blink::DartServiceIsolate::GetObservatoryUri().c_str()); @@ -545,202 +576,171 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, ); } -bool PlatformViewAndroid::Register(JNIEnv* env) { - if (env == nullptr) { - return false; - } - - g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); - if (g_flutter_callback_info_class->is_null()) { - return false; - } - - g_flutter_callback_info_constructor = env->GetMethodID( - g_flutter_callback_info_class->obj(), "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - if (g_flutter_callback_info_constructor == nullptr) { - return false; - } - +bool RegisterOldApi(JNIEnv* env) { g_flutter_view_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass("io/flutter/view/FlutterView")); if (g_flutter_view_class->is_null()) { + FML_LOG(ERROR) << "Could not locate FlutterView class"; return false; } g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass("io/flutter/view/FlutterNativeView")); if (g_flutter_native_view_class->is_null()) { - return false; - } - - g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("android/graphics/SurfaceTexture")); - if (g_surface_texture_class->is_null()) { + FML_LOG(ERROR) << "Could not locate FlutterNativeView class"; return false; } static const JNINativeMethod native_view_methods[] = { - { - .name = "nativeAttach", - .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", - .fnPtr = reinterpret_cast(&shell::Attach), - }, - { - .name = "nativeDestroy", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), - }, - { - .name = "nativeRunBundleAndSnapshotFromLibrary", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Landroid/content/res/AssetManager;)V", - .fnPtr = - reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), - }, - { - .name = "nativeDetach", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Detach), - }, - { - .name = "nativeGetObservatoryUri", - .signature = "()Ljava/lang/String;", - .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), - }, - { - .name = "nativeDispatchEmptyPlatformMessage", - .signature = "(JLjava/lang/String;I)V", - .fnPtr = - reinterpret_cast(&shell::DispatchEmptyPlatformMessage), - }, - { - .name = "nativeDispatchPlatformMessage", - .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", - .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), - }, - { - .name = "nativeInvokePlatformMessageResponseCallback", - .signature = "(JILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageResponseCallback), - }, - { - .name = "nativeInvokePlatformMessageEmptyResponseCallback", - .signature = "(JI)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageEmptyResponseCallback), - }, + { + .name = "nativeAttach", + .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", + .fnPtr = reinterpret_cast(&shell::Attach), + }, + { + .name = "nativeDestroy", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::Destroy), + }, + { + .name = "nativeRunBundleAndSnapshotFromLibrary", + .signature = + "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Landroid/content/res/AssetManager;)V", + .fnPtr = + reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), + }, + { + .name = "nativeDetach", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::Detach), + }, + { + .name = "nativeGetObservatoryUri", + .signature = "()Ljava/lang/String;", + .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), + }, + { + .name = "nativeDispatchEmptyPlatformMessage", + .signature = "(JLjava/lang/String;I)V", + .fnPtr = + reinterpret_cast(&shell::DispatchEmptyPlatformMessage), + }, + { + .name = "nativeDispatchPlatformMessage", + .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", + .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), + }, + { + .name = "nativeInvokePlatformMessageResponseCallback", + .signature = "(JILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageResponseCallback), + }, + { + .name = "nativeInvokePlatformMessageEmptyResponseCallback", + .signature = "(JI)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageEmptyResponseCallback), + }, }; static const JNINativeMethod view_methods[] = { - { - .name = "nativeSurfaceCreated", - .signature = "(JLandroid/view/Surface;I)V", - .fnPtr = reinterpret_cast(&shell::SurfaceCreated), - }, - { - .name = "nativeSurfaceChanged", - .signature = "(JII)V", - .fnPtr = reinterpret_cast(&shell::SurfaceChanged), - }, - { - .name = "nativeSurfaceDestroyed", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), - }, - { - .name = "nativeSetViewportMetrics", - .signature = "(JFIIIIIIIIII)V", - .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), - }, - { - .name = "nativeGetBitmap", - .signature = "(J)Landroid/graphics/Bitmap;", - .fnPtr = reinterpret_cast(&shell::GetBitmap), - }, - { - .name = "nativeDispatchPointerDataPacket", - .signature = "(JLjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), - }, - { - .name = "nativeDispatchSemanticsAction", - .signature = "(JIILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), - }, - { - .name = "nativeSetSemanticsEnabled", - .signature = "(JZ)V", - .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), - }, - { - .name = "nativeSetAccessibilityFeatures", - .signature = "(JI)V", - .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), - }, - { - .name = "nativeGetIsSoftwareRenderingEnabled", - .signature = "()Z", - .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), - }, - { - .name = "nativeRegisterTexture", - .signature = "(JJLandroid/graphics/SurfaceTexture;)V", - .fnPtr = reinterpret_cast(&shell::RegisterTexture), - }, - { - .name = "nativeMarkTextureFrameAvailable", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), - }, - { - .name = "nativeUnregisterTexture", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::UnregisterTexture), - }, - }; - - static const JNINativeMethod callback_info_methods[] = { - { - .name = "nativeLookupCallbackInformation", - .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", - .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), - }, + { + .name = "nativeSurfaceCreated", + .signature = "(JLandroid/view/Surface;I)V", + .fnPtr = reinterpret_cast(&shell::SurfaceCreated), + }, + { + .name = "nativeSurfaceChanged", + .signature = "(JII)V", + .fnPtr = reinterpret_cast(&shell::SurfaceChanged), + }, + { + .name = "nativeSurfaceDestroyed", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), + }, + { + .name = "nativeSetViewportMetrics", + .signature = "(JFIIIIIIIIII)V", + .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), + }, + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, + { + .name = "nativeDispatchPointerDataPacket", + .signature = "(JLjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), + }, + { + .name = "nativeDispatchSemanticsAction", + .signature = "(JIILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), + }, + { + .name = "nativeSetSemanticsEnabled", + .signature = "(JZ)V", + .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), + }, + { + .name = "nativeSetAccessibilityFeatures", + .signature = "(JI)V", + .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), + }, + { + .name = "nativeGetIsSoftwareRenderingEnabled", + .signature = "()Z", + .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), + }, + { + .name = "nativeRegisterTexture", + .signature = "(JJLandroid/graphics/SurfaceTexture;)V", + .fnPtr = reinterpret_cast(&shell::RegisterTexture), + }, + { + .name = "nativeMarkTextureFrameAvailable", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), + }, + { + .name = "nativeUnregisterTexture", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::UnregisterTexture), + }, }; if (env->RegisterNatives(g_flutter_native_view_class->obj(), - native_view_methods, - arraysize(native_view_methods)) != 0) { + native_view_methods, + arraysize(native_view_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterNativeView."; return false; } if (env->RegisterNatives(g_flutter_view_class->obj(), view_methods, arraysize(view_methods)) != 0) { - return false; - } - - if (env->RegisterNatives(g_flutter_callback_info_class->obj(), - callback_info_methods, - arraysize(callback_info_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterView"; return false; } g_handle_platform_message_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + env->GetMethodID(g_flutter_native_view_class->obj(), + "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; return false; } g_handle_platform_message_response_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessageResponse", "(I[B)V"); + env->GetMethodID(g_flutter_native_view_class->obj(), + "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; return false; } @@ -749,6 +749,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_semantics_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateSemantics method"; return false; } @@ -757,6 +758,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateCustomAccessibilityActions method"; return false; } @@ -764,6 +766,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { "onFirstFrame", "()V"); if (g_on_first_frame_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onFirstFrame method"; return false; } @@ -771,6 +774,255 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_flutter_native_view_class->obj(), "onPreEngineRestart", "()V"); if (g_on_engine_restart_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onEngineRestart method"; + return false; + } + + return true; +} + +bool RegisterNewApi(JNIEnv* env) { + g_flutter_engine_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/embedding/FlutterEngine")); + if (g_flutter_engine_class->is_null()) { + FML_LOG(ERROR) << "Failed to find FlutterEngine Class."; + return false; + } + + static const JNINativeMethod flutter_jni_methods[] = { + // Start of methods from FlutterNativeView + { + .name = "nativeAttach", + .signature = "(Lio/flutter/embedding/FlutterEngine;Z)J", + .fnPtr = reinterpret_cast(&shell::AttachJNI), + }, + { + .name = "nativeDetach", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::DetachJNI), + }, + { + .name = "nativeDestroy", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::DestroyJNI), + }, + { + .name = "nativeRunBundleAndSnapshotFromLibrary", + .signature = + "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Landroid/content/res/AssetManager;)V", + .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), + }, + { + .name = "nativeGetObservatoryUri", + .signature = "()Ljava/lang/String;", + .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), + }, + { + .name = "nativeDispatchEmptyPlatformMessage", + .signature = "(JLjava/lang/String;I)V", + .fnPtr = + reinterpret_cast(&shell::DispatchEmptyPlatformMessage), + }, + { + .name = "nativeDispatchPlatformMessage", + .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", + .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), + }, + { + .name = "nativeInvokePlatformMessageResponseCallback", + .signature = "(JILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageResponseCallback), + }, + { + .name = "nativeInvokePlatformMessageEmptyResponseCallback", + .signature = "(JI)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageEmptyResponseCallback), + }, + + // Start of methods from FlutterView + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, + { + .name = "nativeSurfaceCreated", + .signature = "(JLandroid/view/Surface;I)V", + .fnPtr = reinterpret_cast(&shell::SurfaceCreated), + }, + { + .name = "nativeSurfaceChanged", + .signature = "(JII)V", + .fnPtr = reinterpret_cast(&shell::SurfaceChanged), + }, + { + .name = "nativeSurfaceDestroyed", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), + }, + { + .name = "nativeSetViewportMetrics", + .signature = "(JFIIIIIIIIII)V", + .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), + }, + { + .name = "nativeDispatchPointerDataPacket", + .signature = "(JLjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), + }, + { + .name = "nativeDispatchSemanticsAction", + .signature = "(JIILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), + }, + { + .name = "nativeSetSemanticsEnabled", + .signature = "(JZ)V", + .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), + }, + { + .name = "nativeSetAccessibilityFeatures", + .signature = "(JI)V", + .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), + }, + { + .name = "nativeGetIsSoftwareRenderingEnabled", + .signature = "()Z", + .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), + }, + { + .name = "nativeRegisterTexture", + .signature = "(JJLandroid/graphics/SurfaceTexture;)V", + .fnPtr = reinterpret_cast(&shell::RegisterTexture), + }, + { + .name = "nativeMarkTextureFrameAvailable", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), + }, + { + .name = "nativeUnregisterTexture", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::UnregisterTexture), + }, + }; + + if (env->RegisterNatives(g_flutter_jni_class->obj(), + flutter_jni_methods, + arraysize(flutter_jni_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI"; + return false; + } + + g_handle_platform_message_method = + env->GetMethodID(g_flutter_engine_class->obj(), + "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + + if (g_handle_platform_message_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; + return false; + } + + g_handle_platform_message_response_method = + env->GetMethodID(g_flutter_engine_class->obj(), + "handlePlatformMessageResponse", "(I[B)V"); + + if (g_handle_platform_message_response_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; + return false; + } + + g_update_semantics_method = + env->GetMethodID(g_flutter_engine_class->obj(), "updateSemantics", + "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); + + if (g_update_semantics_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateSemantics method"; + return false; + } + + g_update_custom_accessibility_actions_method = env->GetMethodID( + g_flutter_engine_class->obj(), "updateCustomAccessibilityActions", + "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); + + if (g_update_custom_accessibility_actions_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateCustomAccessibilityActions method"; + return false; + } + + g_on_first_frame_method = env->GetMethodID(g_flutter_engine_class->obj(), + "onFirstFrame", "()V"); + + if (g_on_first_frame_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onFirstFrame method"; + return false; + } + + g_on_engine_restart_method = env->GetMethodID( + g_flutter_engine_class->obj(), "onPreEngineRestart", "()V"); + + if (g_on_engine_restart_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onEngineRestart method"; + return false; + } + + return true; +} + +bool PlatformViewAndroid::Register(JNIEnv* env) { + FML_LOG(ERROR) << "Registering"; + if (env == nullptr) { + FML_LOG(ERROR) << "No JNIEnv provided"; + return false; + } + + g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); + if (g_flutter_callback_info_class->is_null()) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation class"; + return false; + } + + g_flutter_callback_info_constructor = env->GetMethodID( + g_flutter_callback_info_class->obj(), "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + if (g_flutter_callback_info_constructor == nullptr) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation constructor"; + return false; + } + + // TODO(mattcarroll): this assumes the use of the new API is based on what we're compiling against but that's not true. Need to support both simultaneously. + bool is_using_new_api = false; + g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/embedding/FlutterJNI")); + if (g_flutter_jni_class->is_null()) { + FML_LOG(ERROR) << "Failed to find FlutterJNI Class. Assuming old API"; + } else { + is_using_new_api = true; + } + + g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("android/graphics/SurfaceTexture")); + if (g_surface_texture_class->is_null()) { + FML_LOG(ERROR) << "Could not locate SurfaceTexture class"; + return false; + } + + static const JNINativeMethod callback_info_methods[] = { + { + .name = "nativeLookupCallbackInformation", + .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", + .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), + }, + }; + + if (env->RegisterNatives(g_flutter_callback_info_class->obj(), + callback_info_methods, + arraysize(callback_info_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterCallbackInfo"; return false; } @@ -778,6 +1030,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "attachToGLContext", "(I)V"); if (g_attach_to_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate attachToGlContext method"; return false; } @@ -785,6 +1038,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { env->GetMethodID(g_surface_texture_class->obj(), "updateTexImage", "()V"); if (g_update_tex_image_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateTexImage method"; return false; } @@ -792,6 +1046,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "getTransformMatrix", "([F)V"); if (g_get_transform_matrix_method == nullptr) { + FML_LOG(ERROR) << "Could not locate getTransformMatrix method"; return false; } @@ -799,10 +1054,15 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "detachFromGLContext", "()V"); if (g_detach_from_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate detachFromGlContext method"; return false; } - return true; + if (is_using_new_api) { + return RegisterNewApi(env); + } else { + return RegisterOldApi(env); + } } } // namespace shell From 4e7154ff997f4587d6d155dde15322d80d5254dd Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 6 Sep 2018 23:50:23 -0700 Subject: [PATCH 06/11] Added comments to FlutterEngine, FlutterRender, FlutterFragment, and FlutterView. --- .../io/flutter/embedding/FlutterEngine.java | 173 ++++++++++-------- .../io/flutter/embedding/FlutterFragment.java | 79 ++------ .../io/flutter/embedding/FlutterRenderer.java | 76 ++++++-- .../io/flutter/embedding/FlutterView.java | 110 +++++++---- 4 files changed, 244 insertions(+), 194 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java index 3a002dfacabfc..f7cfb995aa97c 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -3,10 +3,7 @@ import android.app.Activity; import android.content.Context; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.SurfaceTexture; import android.util.Log; -import android.view.Surface; import java.nio.ByteBuffer; import java.util.HashMap; @@ -17,14 +14,37 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.view.FlutterRunArguments; +/** + * A single Flutter execution environment. + * + * A {@code FlutterEngine} can execute in the background, or it can be rendered to the screen by + * using the accompanying {@link FlutterRenderer}. Rendering can be started and stopped, thus + * allowing a {@code FlutterEngine} to move from UI interaction to data-only processing and then + * back to UI interaction. + * + * To start running Flutter within this {@code FlutterEngine}, use {@link #runFromBundle(FlutterRunArguments)}. + * The {@link #runFromBundle(FlutterRunArguments)} method may not be invoked twice on the same + * {@code FlutterEngine}. + * + * To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a + * {@link FlutterRenderer} and then attach a {@link FlutterRenderer.RenderSurface}. Consider using + * a {@link FlutterView} as a {@link FlutterRenderer.RenderSurface}. + * TODO(mattcarroll): for the above instructions for RenderSurface to be true, we need to refactor + * the native relationships and also the PlatformViewsController relationship. + */ public class FlutterEngine implements BinaryMessenger { private static final String TAG = "FlutterEngine"; + private final Resources resources; + private final FlutterJNI flutterJNI; + private final FlutterRenderer renderer; + private final FlutterPluginRegistry pluginRegistry; + private final Map mMessageHandlers; + private final Map mPendingReplies = new HashMap<>(); private long nativeObjectReference; - private FlutterJNI flutterJNI; - private FlutterRenderer renderer; - private FlutterPluginRegistry pluginRegistry; private boolean isBackgroundView; // TODO(mattcarroll): rename to something without "view" + private boolean applicationIsRunning; + private int mNextReplyId = 1; FlutterEngine( Context context, @@ -34,55 +54,35 @@ public class FlutterEngine implements BinaryMessenger { this.flutterJNI = new FlutterJNI(); this.resources = resources; this.isBackgroundView = isBackgroundView; - pluginRegistry = new FlutterPluginRegistry(this, context); - attach(); - assertAttached(); - // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling. - this.renderer = new FlutterRenderer(this, flutterJNI, nativeObjectReference); mMessageHandlers = new HashMap<>(); - } - public FlutterRenderer getRenderer() { - return renderer; + attach(); + // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if possible. + this.renderer = new FlutterRenderer(flutterJNI, nativeObjectReference); } - //------- START PLUGINS ------ - public FlutterPluginRegistry getPluginRegistry() { - return pluginRegistry; - } - //------- END PLUGINS ----- + private void attach() { + // TODO(mattcarroll): what impact does "isBackgroundView' have? + nativeObjectReference = flutterJNI.nativeAttach(this, isBackgroundView); - //------- START ISOLATE METHOD CHANNEL COMMS ------ - @Override - public void setMessageHandler(String channel, BinaryMessageHandler handler) { - if (handler == null) { - mMessageHandlers.remove(channel); - } else { - mMessageHandlers.put(channel, handler); + if (!isAttached()) { + throw new RuntimeException("FlutterEngine failed to attach to its native Object reference."); } } - @Override - public void send(String channel, ByteBuffer message) { - send(channel, message, null); + private void assertAttached() { + if (!isAttached()) throw new AssertionError("Platform view is not attached"); } - //------- END ISOLATE METHOD CHANNEL COMMS ----- - //------- START COPY FROM FlutterNativeView ----- - private final Map mMessageHandlers; - private int mNextReplyId = 1; - private final Map mPendingReplies = new HashMap<>(); - - private final Resources resources; - private boolean applicationIsRunning; - - private void attach() { - nativeObjectReference = flutterJNI.nativeAttach(this, isBackgroundView); + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean isAttached() { + return nativeObjectReference != 0; } public void detach() { pluginRegistry.detach(); + // TODO(mattcarroll): why do we have a nativeDetach() method? can we get rid of this? flutterJNI.nativeDetach(nativeObjectReference); } @@ -93,22 +93,6 @@ public void destroy() { applicationIsRunning = false; } - public void attachViewAndActivity(Activity activity) { - pluginRegistry.attach(this, activity); - } - - public boolean isAttached() { - return nativeObjectReference != 0; - } - - public long get() { - return nativeObjectReference; - } - - public void assertAttached() { - if (!isAttached()) throw new AssertionError("Platform view is not attached"); - } - public void runFromBundle(FlutterRunArguments args) { if (args.bundlePath == null) { throw new AssertionError("A bundlePath must be specified"); @@ -118,23 +102,18 @@ public void runFromBundle(FlutterRunArguments args) { runFromBundleInternal(args.bundlePath, args.entrypoint, args.libraryPath, args.defaultPath); } - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, - boolean reuseRuntimeController) { - runFromBundleInternal(bundlePath, entrypoint, null, defaultPath); - } - - private void runFromBundleInternal(String bundlePath, String entrypoint, - String libraryPath, String defaultPath) { + private void runFromBundleInternal( + String bundlePath, + String entrypoint, + String libraryPath, + String defaultPath + ) { assertAttached(); - if (applicationIsRunning) - throw new AssertionError( - "This Flutter engine instance is already running an application"); + + if (applicationIsRunning) { + throw new AssertionError("This Flutter engine instance is already running an application"); + } + flutterJNI.nativeRunBundleAndSnapshotFromLibrary(nativeObjectReference, bundlePath, defaultPath, entrypoint, libraryPath, resources.getAssets()); @@ -145,10 +124,48 @@ public boolean isApplicationRunning() { return applicationIsRunning; } + // TODO(mattcarroll): what does this callback actually represent? + // Called by native to notify when the engine is restarted (cold reload). + @SuppressWarnings("unused") + private void onPreEngineRestart() { + if (pluginRegistry == null) + return; + pluginRegistry.onPreEngineRestart(); + } + + public FlutterRenderer getRenderer() { + return renderer; + } + + public FlutterPluginRegistry getPluginRegistry() { + return pluginRegistry; + } + + // TODO(mattcarroll): Is this method really all about plugins? or does it have other implications? + public void attachViewAndActivity(Activity activity) { + pluginRegistry.attach(this, activity); + } + + // TODO(mattcarroll): This method says it's unused. Is that true? Do we need it? public String getObservatoryUri() { return flutterJNI.nativeGetObservatoryUri(); } + //------- START ISOLATE METHOD CHANNEL COMMS ------ + @Override + public void setMessageHandler(String channel, BinaryMessageHandler handler) { + if (handler == null) { + mMessageHandlers.remove(channel); + } else { + mMessageHandlers.put(channel, handler); + } + } + + @Override + public void send(String channel, ByteBuffer message) { + send(channel, message, null); + } + @Override public void send(String channel, ByteBuffer message, BinaryReply callback) { if (!isAttached()) { @@ -218,7 +235,9 @@ private void handlePlatformMessageResponse(int replyId, byte[] reply) { } } } + //------- END ISOLATE METHOD CHANNEL COMMS ----- + //------ START NATIVE CALLBACKS THAT SHOULD BE MOVED TO FlutterRenderer ------ // Called by native to update the semantics/accessibility tree. @SuppressWarnings("unused") private void updateSemantics(ByteBuffer buffer, String[] strings) { @@ -239,13 +258,5 @@ private void onFirstFrame() { Log.d(TAG, "onFirstFrame()"); renderer.onFirstFrameRendered(); } - - // Called by native to notify when the engine is restarted (cold reload). - @SuppressWarnings("unused") - private void onPreEngineRestart() { - if (pluginRegistry == null) - return; - pluginRegistry.onPreEngineRestart(); - } - //------- END COPY FROM FlutterNativeView ---- + //------ END NATIVE CALLBACKS THAT SHOULD BE MOVED TO FlutterRenderer ------ } diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index 7bdbb37f1882b..bfdaf2a153c3c 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -399,18 +399,6 @@ private Drawable getSplashScreenDrawableFromActivityTheme() { } } - /** - * Returns the Flutter view used by this {@code Fragment}; will be null before - * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} is invoked. Will be - * non-null after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} is invoked, up - * until {@link #onDestroyView()} is invoked. - */ - @SuppressWarnings("unused") - @Nullable - public FlutterView getFlutterView() { - return flutterView; - } - /** * Starts running Dart within the FlutterView for the first time. * @@ -425,7 +413,16 @@ private void doInitialFlutterViewRun() { if (getInitialRoute() != null) { setInitialRoute(getInitialRoute()); } - runFromBundle(getAppBundlePath(), null, "main", false); + + // TODO(mattcarroll): are FlutterRunArguments and FlutterShellArgs the same thing? consolidate if they are + FlutterRunArguments args = new FlutterRunArguments(); + args.bundlePath = getAppBundlePath(); + args.entrypoint = "main"; + args.defaultPath = null; + flutterEngine.runFromBundle(args); + + // TODO(mattcarroll): why do we need to resetAccessibilityTree in this method? Can we call that from within FlutterView somewhere? + flutterView.resetAccessibilityTree(); } @Nullable @@ -473,65 +470,15 @@ private Context getContextCompat() { // : getActivity(); } - public void setInitialRoute(String route) { + private void setInitialRoute(String route) { mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); } - public void pushRoute(String route) { + private void pushRoute(String route) { mFlutterNavigationChannel.invokeMethod("pushRoute", route); } - public void popRoute() { + private void popRoute() { mFlutterNavigationChannel.invokeMethod("popRoute", null); } - - //------ START RUN FROM BUNDLE ----- - public void runFromBundle(FlutterRunArguments args) { - assertFlutterEngineAttached(); - // TODO(mattcarroll): why do we need to resetAccessibilityTree here? Can we call that from within FlutterView somewhere? - flutterView.resetAccessibilityTree(); - flutterEngine.runFromBundle(args); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath) { - runFromBundle(bundlePath, defaultPath, "main", false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint) { - runFromBundle(bundlePath, defaultPath, entrypoint, false); - } - - /** - * @deprecated - * Please use runFromBundle with `FlutterRunArguments`. - * Parameter `reuseRuntimeController` has no effect. - */ - @Deprecated - public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = bundlePath; - args.entrypoint = entrypoint; - args.defaultPath = defaultPath; - runFromBundle(args); - } - - private boolean isFlutterEngineAttached() { - return flutterEngine != null && flutterEngine.isAttached(); - } - - void assertFlutterEngineAttached() { - if (!isFlutterEngineAttached()) - throw new AssertionError("Platform view is not attached"); - } - //------ END RUN FROM BUNDLE ---- } diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java index 259308eabd7a8..c25f437fb9548 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -14,20 +14,26 @@ import io.flutter.view.TextureRegistry; +/** + * {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to create an + * interactive Flutter UI. + * + * {@code FlutterRenderer} manages textures for rendering, and forwards messages to native Flutter + * code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out + * certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy. + * + * {@link FlutterView} is an implementation of a {@link RenderSurface}. + */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class FlutterRenderer implements TextureRegistry { - private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); - private final FlutterEngine flutterEngine; private final FlutterJNI flutterJNI; private final long nativeObjectReference; + private final AtomicLong nextTextureId = new AtomicLong(0L); + private final Set firstFrameListeners = new CopyOnWriteArraySet<>(); private RenderSurface renderSurface; - FlutterRenderer( - @NonNull FlutterEngine flutterEngine, - @NonNull FlutterJNI flutterJNI, - long nativeObjectReference) { - this.flutterEngine = flutterEngine; + FlutterRenderer(@NonNull FlutterJNI flutterJNI, long nativeObjectReference) { this.flutterJNI = flutterJNI; this.nativeObjectReference = nativeObjectReference; } @@ -63,8 +69,9 @@ private void notifyFirstFrameListeners() { } //------ START TextureRegistry IMPLEMENTATION ----- - private final AtomicLong nextTextureId = new AtomicLong(0L); - + // TODO(mattcarroll): this method probably shouldn't be public. It's part of an obscure relationship + // with PlatformViewsController. Re-evaluate that relationship and see if we can get rid of + // PlatformViewsController. // TODO(mattcarroll): detachFromGLContext requires API 16. what should we do for earlier APIs? @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override @@ -110,26 +117,29 @@ public void release() { if (released) { return; } - released = true; unregisterTexture(id); surfaceTexture.release(); + released = true; } } //------ END TextureRegistry IMPLEMENTATION ---- - //------ START MIGRATION FROM FlutterEngine to FlutterRenderer ----- + // TODO(mattcarroll): what exactly is this method intended to do? public void surfaceCreated(Surface surface, int backgroundColor) { flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface, backgroundColor); } + // TODO(mattcarroll): what exactly is this method intended to do? public void surfaceChanged(int width, int height) { flutterJNI.nativeSurfaceChanged(nativeObjectReference, width, height); } + // TODO(mattcarroll): what exactly is this method intended to do? public void surfaceDestroyed() { flutterJNI.nativeSurfaceDestroyed(nativeObjectReference); } + // TODO(mattcarroll): what exactly is this method intended to do? public void setViewportMetrics(float devicePixelRatio, int physicalWidth, int physicalHeight, @@ -157,38 +167,47 @@ public void setViewportMetrics(float devicePixelRatio, ); } + // TODO(mattcarroll): why does this method exist? public Bitmap getBitmap() { return flutterJNI.nativeGetBitmap(nativeObjectReference); } + // TODO(mattcarroll): what exactly is this method intended to do? public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { flutterJNI.nativeDispatchPointerDataPacket(nativeObjectReference, buffer, position); } - public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + // TODO(mattcarroll): what exactly is this method intended to do? + private void registerTexture(long textureId, SurfaceTexture surfaceTexture) { flutterJNI.nativeRegisterTexture(nativeObjectReference, textureId, surfaceTexture); } - public void markTextureFrameAvailable(long textureId) { + // TODO(mattcarroll): what exactly is this method intended to do? + private void markTextureFrameAvailable(long textureId) { flutterJNI.nativeMarkTextureFrameAvailable(nativeObjectReference, textureId); } - public void unregisterTexture(long textureId) { + // TODO(mattcarroll): what exactly is this method intended to do? + private void unregisterTexture(long textureId) { flutterJNI.nativeUnregisterTexture(nativeObjectReference, textureId); } + // TODO(mattcarroll): what exactly is this method intended to do? public boolean isSoftwareRenderingEnabled() { return flutterJNI.nativeGetIsSoftwareRenderingEnabled(); } + // TODO(mattcarroll): what exactly is this method intended to do? public void setAccessibilityFeatures(int flags) { flutterJNI.nativeSetAccessibilityFeatures(nativeObjectReference, flags); } + // TODO(mattcarroll): what exactly is this method intended to do? public void setSemanticsEnabled(boolean enabled) { flutterJNI.nativeSetSemanticsEnabled(nativeObjectReference, enabled); } + // TODO(mattcarroll): what exactly is this method intended to do? public void dispatchSemanticsAction(int id, int action, ByteBuffer args, @@ -201,8 +220,8 @@ public void dispatchSemanticsAction(int id, argsPosition ); } - //------ END MIGRATION FROM FlutterEngine to FlutterRenderer ---- + // TODO(mattcarroll): change the JNI code to call these directly rather than forward these calls from FlutterEngine //------ START PACKAGE PRIVATE MESSAGES FROM FlutterEngine ------ void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { if (renderSurface != null) { @@ -225,14 +244,41 @@ void onFirstFrameRendered() { //------ END PACKAGE PRIVATE MESSAGES FROM FlutterEngine ------ public interface OnFirstFrameRenderedListener { + /** + * A {@link FlutterRenderer} has painted its first frame since being initialized. + * + * This method will not be invoked if this listener is added after the first frame is rendered. + */ void onFirstFrameRendered(); } + /** + * Delegate used in conjunction with a {@link FlutterRenderer} to create an interactive Flutter + * UI. + * + * A {@code RenderSurface} is responsible for carrying out behaviors that are needed by a + * corresponding {@link FlutterRenderer}, e.g., {@link #updateSemantics(ByteBuffer, String[])}. + * + * A {@code RenderSurface} also receives callbacks for important events, e.g., + * {@link #onFirstFrameRendered()}. + */ public interface RenderSurface { + // TODO(mattcarroll): what is this method supposed to do? void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings); + // TODO(mattcarroll): what is this method supposed to do? void updateSemantics(ByteBuffer buffer, String[] strings); + /** + * The {@link FlutterRenderer} corresponding to this {@code RenderSurface} has painted its + * first frame since being initialized. + * + * "Initialized" refers to Flutter engine initialization, not the first frame after attaching + * to the {@link FlutterRenderer}. Therefore, the first frame may have already rendered by + * the time a {@code RenderSurface} has called {@link #attachToRenderSurface(RenderSurface)} + * on a {@link FlutterRenderer}. In such a situation, {@link #onFirstFrameRendered()} will + * never be called. + */ void onFirstFrameRendered(); } } diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index 90d9bcb318868..6a03ba5833993 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -52,7 +52,31 @@ import io.flutter.view.VsyncWaiter; /** - * An Android view containing a Flutter app. + * {@code View} which can render a Flutter UI by attaching itself to a {@link FlutterRenderer}. + * + * {@code FlutterView} does not take in any Flutter references within its constructor. This is done + * so that {@code FlutterView} can be instantiated from XML, and recreated on configuration change + * without introducing asymmetric APIs for construction. As a result, users of {@code FlutterView} + * must explicitly attach to Flutter, and detach from Flutter, as desired. + * + * To start rendering a Flutter UI, use {@link #attachToFlutterRenderer(FlutterRenderer, BinaryMessenger)}. + * + * To stop rendering a Flutter UI, use {@link #detachFromFlutterRenderer()}. + * + * {@code FlutterView} extends {@link SurfaceView} because Flutter renders directly to a + * {@link android.view.Surface}, which is then displayed by this {@link SurfaceView}. + * + * {@code FlutterView} implements {@link FlutterRenderer.RenderSurface} so that it can operate with + * a {@link FlutterRenderer} to render an interactive Flutter UI. {@code FlutterView} sends commands + * to its {@link FlutterRenderer}, and the {@link FlutterRenderer} sends updates to this + * {@code FlutterView} by utilizing it as a {@link FlutterRenderer.RenderSurface}. + * + * See also + * - {@link SurfaceView}, which displays {@link android.view.Surface}s. + * - {@link FlutterRenderer.RenderSurface}, which is the interface that is implemented by anything + * that wants to render a Flutter UI. + * - {@link android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener}, which + * receives callbacks when Android accessibility settings are changed. */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class FlutterView extends SurfaceView implements @@ -116,6 +140,9 @@ static final class ViewportMetrics { private AccessibilityBridge mAccessibilityNodeProvider; private TouchExplorationListener mTouchExplorationListener; + // Connects the {@code Surface} beneath this {@code SurfaceView} with Flutter's native code. + // Callbacks are received by this Object and then those messages are forwarded to our + // FlutterRenderer, and then on to the JNI bridge over to native Flutter code. private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { @@ -559,30 +586,6 @@ private void resetWillNotDraw() { //----- END AccessibilityStateChangeListener ---- //------- START ACCESSIBILITY ------ - // Called by native to update the semantics/accessibility tree. - @SuppressWarnings("unused") - public void updateSemantics(ByteBuffer buffer, String[] strings) { - try { - if (mAccessibilityNodeProvider != null) { - buffer.order(ByteOrder.LITTLE_ENDIAN); - mAccessibilityNodeProvider.updateSemantics(buffer, strings); - } - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception while updating semantics", ex); - } - } - - public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - try { - if (mAccessibilityNodeProvider != null) { - buffer.order(ByteOrder.LITTLE_ENDIAN); - mAccessibilityNodeProvider.updateCustomAccessibilityActions(buffer, strings); - } - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception while updating local context actions", ex); - } - } - public void dispatchSemanticsAction(int id, AccessibilityBridge.Action action) { dispatchSemanticsAction(id, action, null); } @@ -631,6 +634,14 @@ void resetAccessibilityTree() { //------- END ACCESSIBILITY ---- //----- START FLUTTER INTEGRATION ----- + + /** + * Start rendering the UI for the given {@link FlutterRenderer}. + * + * @param flutterRenderer the FlutterRenderer for which this FlutterView will be a RenderSurface + * @param pluginMessenger the BinaryMessenger to use with any plugins that this FlutterView needs + * to register + */ public void attachToFlutterRenderer(@NonNull FlutterRenderer flutterRenderer, @NonNull BinaryMessenger pluginMessenger) { if (isAttachedToRenderer) { detachFromFlutterRenderer(); @@ -640,9 +651,9 @@ public void attachToFlutterRenderer(@NonNull FlutterRenderer flutterRenderer, @N this.flutterRenderer = flutterRenderer; this.pluginMessenger = pluginMessenger; + // Instruct our FlutterRenderer that we are now its designated RenderSurface. this.flutterRenderer.attachToRenderSurface(this); - - mIsSoftwareRenderingEnabled = flutterRenderer.isSoftwareRenderingEnabled(); + isAttachedToRenderer = true; // Configure the platform plugins and flutter channels. mFlutterLocalizationChannel = new MethodChannel(pluginMessenger, "flutter/localization", JSONMethodCodec.INSTANCE); @@ -653,29 +664,40 @@ public void attachToFlutterRenderer(@NonNull FlutterRenderer flutterRenderer, @N setFlutterLocale(getResources().getConfiguration().locale); setFlutterUserSettings(); + // Grab a reference to our underlying Surface and register callbacks with that Surface so we + // can monitor changes and forward those changes on to native Flutter code. getHolder().addCallback(mSurfaceCallback); - isAttachedToRenderer = true; + mIsSoftwareRenderingEnabled = flutterRenderer.isSoftwareRenderingEnabled(); } private void assertAttachedToFlutterRenderer() { - if (!isAttachedToRenderer) + if (!isAttachedToRenderer) { throw new AssertionError("FlutterView is not attached to a FlutterRenderer."); + } } + /** + * Stop rendering the UI for a given {@link FlutterRenderer}. + * + * If no {@link FlutterRenderer} is currently attached, this method does nothing. + */ public void detachFromFlutterRenderer() { - if (!isAttachedToRenderer) + if (!isAttachedToRenderer) { return; + } + // Stop forwarding messages from our underlying Surface to native Flutter code. getHolder().removeCallback(mSurfaceCallback); + // Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface. flutterRenderer.detachFromRenderSurface(); - + flutterRenderer = null; isAttachedToRenderer = false; } /** - * Send this Android device's {@link Locale} configuration to Flutter. + * Send the given {@link Locale} configuration to Flutter. * @param locale the user's locale */ private void setFlutterLocale(@NonNull Locale locale) { @@ -697,6 +719,30 @@ private void setFlutterUserSettings() { //----- END FLUTTER INTEGRATION ----- //------ START RenderingSurface ----- + @Override + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + try { + if (mAccessibilityNodeProvider != null) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + mAccessibilityNodeProvider.updateCustomAccessibilityActions(buffer, strings); + } + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception while updating local context actions", ex); + } + } + + @Override + public void updateSemantics(ByteBuffer buffer, String[] strings) { + try { + if (mAccessibilityNodeProvider != null) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + mAccessibilityNodeProvider.updateSemantics(buffer, strings); + } + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception while updating semantics", ex); + } + } + @Override public void onFirstFrameRendered() { // no-op From d214c378d90a7ac6e41ed2882b2b77530ff085f6 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Fri, 7 Sep 2018 13:51:23 -0700 Subject: [PATCH 07/11] Removed some extraneous code. Adjusted first frame notification slightly. --- .../android/io/flutter/embedding/FlutterFragment.java | 7 ------- .../android/io/flutter/embedding/FlutterRenderer.java | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index bfdaf2a153c3c..62e1b2e023f8d 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -121,12 +121,6 @@ public void onStart() { mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); } - @Override - public void onResume() { - super.onResume(); - Log.d(TAG, "onResume()"); - } - public void onPostResume() { Log.d(TAG, "onPostResume()"); flutterView.updateAccessibilityFeatures(); @@ -243,7 +237,6 @@ private void sendMemoryPressureWarningToFlutter() { */ private void createLayout() { container = new FrameLayout(getContextCompat()); - container.setBackgroundColor(Color.RED); container.setLayoutParams(MATCH_PARENT); flutterView = createFlutterView(getActivity()); diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java index c25f437fb9548..53a5f5ef272f6 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -238,8 +238,8 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { void onFirstFrameRendered() { if (renderSurface != null) { renderSurface.onFirstFrameRendered(); - notifyFirstFrameListeners(); } + notifyFirstFrameListeners(); } //------ END PACKAGE PRIVATE MESSAGES FROM FlutterEngine ------ From 79c5bffd0a00cadc3d876caf365c18412b9a1bac Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Fri, 7 Sep 2018 14:49:18 -0700 Subject: [PATCH 08/11] Fixed formatting in platform_view_android_jni.cc --- .../android/platform_view_android_jni.cc | 532 +++++++++--------- 1 file changed, 270 insertions(+), 262 deletions(-) diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index b1560ab7f6b3f..baa7038cef5ec 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -44,15 +44,20 @@ bool CheckException(JNIEnv* env) { } // anonymous namespace -static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; +static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = + nullptr; -// FlutterView.java, from original embedding API. Not used in 2nd iteration of embedding. +// FlutterView.java, from original embedding API. Not used in 2nd iteration of +// embedding. static fml::jni::ScopedJavaGlobalRef* g_flutter_view_class = nullptr; -// FlutterNativeView.java, from original embedding API. Not used in 2nd iteration of embedding. -static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = nullptr; +// FlutterNativeView.java, from original embedding API. Not used in 2nd +// iteration of embedding. +static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = + nullptr; -// FlutterJNI.java, used in 2nd iteration of embedding to centralize all JNI calls. +// FlutterJNI.java, used in 2nd iteration of embedding to centralize all JNI +// calls. static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; static fml::jni::ScopedJavaGlobalRef* g_flutter_engine_class = nullptr; @@ -592,130 +597,130 @@ bool RegisterOldApi(JNIEnv* env) { } static const JNINativeMethod native_view_methods[] = { - { - .name = "nativeAttach", - .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", - .fnPtr = reinterpret_cast(&shell::Attach), - }, - { - .name = "nativeDestroy", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), - }, - { - .name = "nativeRunBundleAndSnapshotFromLibrary", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Landroid/content/res/AssetManager;)V", - .fnPtr = - reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), - }, - { - .name = "nativeDetach", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Detach), - }, - { - .name = "nativeGetObservatoryUri", - .signature = "()Ljava/lang/String;", - .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), - }, - { - .name = "nativeDispatchEmptyPlatformMessage", - .signature = "(JLjava/lang/String;I)V", - .fnPtr = - reinterpret_cast(&shell::DispatchEmptyPlatformMessage), - }, - { - .name = "nativeDispatchPlatformMessage", - .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", - .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), - }, - { - .name = "nativeInvokePlatformMessageResponseCallback", - .signature = "(JILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageResponseCallback), - }, - { - .name = "nativeInvokePlatformMessageEmptyResponseCallback", - .signature = "(JI)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageEmptyResponseCallback), - }, + { + .name = "nativeAttach", + .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", + .fnPtr = reinterpret_cast(&shell::Attach), + }, + { + .name = "nativeDestroy", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::Destroy), + }, + { + .name = "nativeRunBundleAndSnapshotFromLibrary", + .signature = + "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Landroid/content/res/AssetManager;)V", + .fnPtr = + reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), + }, + { + .name = "nativeDetach", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::Detach), + }, + { + .name = "nativeGetObservatoryUri", + .signature = "()Ljava/lang/String;", + .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), + }, + { + .name = "nativeDispatchEmptyPlatformMessage", + .signature = "(JLjava/lang/String;I)V", + .fnPtr = + reinterpret_cast(&shell::DispatchEmptyPlatformMessage), + }, + { + .name = "nativeDispatchPlatformMessage", + .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", + .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), + }, + { + .name = "nativeInvokePlatformMessageResponseCallback", + .signature = "(JILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageResponseCallback), + }, + { + .name = "nativeInvokePlatformMessageEmptyResponseCallback", + .signature = "(JI)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageEmptyResponseCallback), + }, }; static const JNINativeMethod view_methods[] = { - { - .name = "nativeSurfaceCreated", - .signature = "(JLandroid/view/Surface;I)V", - .fnPtr = reinterpret_cast(&shell::SurfaceCreated), - }, - { - .name = "nativeSurfaceChanged", - .signature = "(JII)V", - .fnPtr = reinterpret_cast(&shell::SurfaceChanged), - }, - { - .name = "nativeSurfaceDestroyed", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), - }, - { - .name = "nativeSetViewportMetrics", - .signature = "(JFIIIIIIIIII)V", - .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), - }, - { - .name = "nativeGetBitmap", - .signature = "(J)Landroid/graphics/Bitmap;", - .fnPtr = reinterpret_cast(&shell::GetBitmap), - }, - { - .name = "nativeDispatchPointerDataPacket", - .signature = "(JLjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), - }, - { - .name = "nativeDispatchSemanticsAction", - .signature = "(JIILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), - }, - { - .name = "nativeSetSemanticsEnabled", - .signature = "(JZ)V", - .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), - }, - { - .name = "nativeSetAccessibilityFeatures", - .signature = "(JI)V", - .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), - }, - { - .name = "nativeGetIsSoftwareRenderingEnabled", - .signature = "()Z", - .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), - }, - { - .name = "nativeRegisterTexture", - .signature = "(JJLandroid/graphics/SurfaceTexture;)V", - .fnPtr = reinterpret_cast(&shell::RegisterTexture), - }, - { - .name = "nativeMarkTextureFrameAvailable", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), - }, - { - .name = "nativeUnregisterTexture", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::UnregisterTexture), - }, + { + .name = "nativeSurfaceCreated", + .signature = "(JLandroid/view/Surface;I)V", + .fnPtr = reinterpret_cast(&shell::SurfaceCreated), + }, + { + .name = "nativeSurfaceChanged", + .signature = "(JII)V", + .fnPtr = reinterpret_cast(&shell::SurfaceChanged), + }, + { + .name = "nativeSurfaceDestroyed", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), + }, + { + .name = "nativeSetViewportMetrics", + .signature = "(JFIIIIIIIIII)V", + .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), + }, + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, + { + .name = "nativeDispatchPointerDataPacket", + .signature = "(JLjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), + }, + { + .name = "nativeDispatchSemanticsAction", + .signature = "(JIILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), + }, + { + .name = "nativeSetSemanticsEnabled", + .signature = "(JZ)V", + .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), + }, + { + .name = "nativeSetAccessibilityFeatures", + .signature = "(JI)V", + .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), + }, + { + .name = "nativeGetIsSoftwareRenderingEnabled", + .signature = "()Z", + .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), + }, + { + .name = "nativeRegisterTexture", + .signature = "(JJLandroid/graphics/SurfaceTexture;)V", + .fnPtr = reinterpret_cast(&shell::RegisterTexture), + }, + { + .name = "nativeMarkTextureFrameAvailable", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), + }, + { + .name = "nativeUnregisterTexture", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::UnregisterTexture), + }, }; if (env->RegisterNatives(g_flutter_native_view_class->obj(), - native_view_methods, - arraysize(native_view_methods)) != 0) { + native_view_methods, + arraysize(native_view_methods)) != 0) { FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterNativeView."; return false; } @@ -727,8 +732,8 @@ bool RegisterOldApi(JNIEnv* env) { } g_handle_platform_message_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + env->GetMethodID(g_flutter_native_view_class->obj(), + "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; @@ -736,8 +741,8 @@ bool RegisterOldApi(JNIEnv* env) { } g_handle_platform_message_response_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessageResponse", "(I[B)V"); + env->GetMethodID(g_flutter_native_view_class->obj(), + "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; @@ -758,7 +763,8 @@ bool RegisterOldApi(JNIEnv* env) { "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { - FML_LOG(ERROR) << "Could not locate updateCustomAccessibilityActions method"; + FML_LOG(ERROR) + << "Could not locate updateCustomAccessibilityActions method"; return false; } @@ -783,152 +789,151 @@ bool RegisterOldApi(JNIEnv* env) { bool RegisterNewApi(JNIEnv* env) { g_flutter_engine_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/embedding/FlutterEngine")); + env, env->FindClass("io/flutter/embedding/FlutterEngine")); if (g_flutter_engine_class->is_null()) { FML_LOG(ERROR) << "Failed to find FlutterEngine Class."; return false; } static const JNINativeMethod flutter_jni_methods[] = { - // Start of methods from FlutterNativeView - { - .name = "nativeAttach", - .signature = "(Lio/flutter/embedding/FlutterEngine;Z)J", - .fnPtr = reinterpret_cast(&shell::AttachJNI), - }, - { - .name = "nativeDetach", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::DetachJNI), - }, - { - .name = "nativeDestroy", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::DestroyJNI), - }, - { - .name = "nativeRunBundleAndSnapshotFromLibrary", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Landroid/content/res/AssetManager;)V", - .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), - }, - { - .name = "nativeGetObservatoryUri", - .signature = "()Ljava/lang/String;", - .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), - }, - { - .name = "nativeDispatchEmptyPlatformMessage", - .signature = "(JLjava/lang/String;I)V", - .fnPtr = - reinterpret_cast(&shell::DispatchEmptyPlatformMessage), - }, - { - .name = "nativeDispatchPlatformMessage", - .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", - .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), - }, - { - .name = "nativeInvokePlatformMessageResponseCallback", - .signature = "(JILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageResponseCallback), - }, - { - .name = "nativeInvokePlatformMessageEmptyResponseCallback", - .signature = "(JI)V", - .fnPtr = reinterpret_cast( - &shell::InvokePlatformMessageEmptyResponseCallback), - }, - - // Start of methods from FlutterView - { - .name = "nativeGetBitmap", - .signature = "(J)Landroid/graphics/Bitmap;", - .fnPtr = reinterpret_cast(&shell::GetBitmap), - }, - { - .name = "nativeSurfaceCreated", - .signature = "(JLandroid/view/Surface;I)V", - .fnPtr = reinterpret_cast(&shell::SurfaceCreated), - }, - { - .name = "nativeSurfaceChanged", - .signature = "(JII)V", - .fnPtr = reinterpret_cast(&shell::SurfaceChanged), - }, - { - .name = "nativeSurfaceDestroyed", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), - }, - { - .name = "nativeSetViewportMetrics", - .signature = "(JFIIIIIIIIII)V", - .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), - }, - { - .name = "nativeDispatchPointerDataPacket", - .signature = "(JLjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), - }, - { - .name = "nativeDispatchSemanticsAction", - .signature = "(JIILjava/nio/ByteBuffer;I)V", - .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), - }, - { - .name = "nativeSetSemanticsEnabled", - .signature = "(JZ)V", - .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), - }, - { - .name = "nativeSetAccessibilityFeatures", - .signature = "(JI)V", - .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), - }, - { - .name = "nativeGetIsSoftwareRenderingEnabled", - .signature = "()Z", - .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), - }, - { - .name = "nativeRegisterTexture", - .signature = "(JJLandroid/graphics/SurfaceTexture;)V", - .fnPtr = reinterpret_cast(&shell::RegisterTexture), - }, - { - .name = "nativeMarkTextureFrameAvailable", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), - }, - { - .name = "nativeUnregisterTexture", - .signature = "(JJ)V", - .fnPtr = reinterpret_cast(&shell::UnregisterTexture), - }, + // Start of methods from FlutterNativeView + { + .name = "nativeAttach", + .signature = "(Lio/flutter/embedding/FlutterEngine;Z)J", + .fnPtr = reinterpret_cast(&shell::AttachJNI), + }, + { + .name = "nativeDetach", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::DetachJNI), + }, + { + .name = "nativeDestroy", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::DestroyJNI), + }, + { + .name = "nativeRunBundleAndSnapshotFromLibrary", + .signature = + "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Landroid/content/res/AssetManager;)V", + .fnPtr = + reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), + }, + { + .name = "nativeGetObservatoryUri", + .signature = "()Ljava/lang/String;", + .fnPtr = reinterpret_cast(&shell::GetObservatoryUri), + }, + { + .name = "nativeDispatchEmptyPlatformMessage", + .signature = "(JLjava/lang/String;I)V", + .fnPtr = + reinterpret_cast(&shell::DispatchEmptyPlatformMessage), + }, + { + .name = "nativeDispatchPlatformMessage", + .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", + .fnPtr = reinterpret_cast(&shell::DispatchPlatformMessage), + }, + { + .name = "nativeInvokePlatformMessageResponseCallback", + .signature = "(JILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageResponseCallback), + }, + { + .name = "nativeInvokePlatformMessageEmptyResponseCallback", + .signature = "(JI)V", + .fnPtr = reinterpret_cast( + &shell::InvokePlatformMessageEmptyResponseCallback), + }, + + // Start of methods from FlutterView + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, + { + .name = "nativeSurfaceCreated", + .signature = "(JLandroid/view/Surface;I)V", + .fnPtr = reinterpret_cast(&shell::SurfaceCreated), + }, + { + .name = "nativeSurfaceChanged", + .signature = "(JII)V", + .fnPtr = reinterpret_cast(&shell::SurfaceChanged), + }, + { + .name = "nativeSurfaceDestroyed", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&shell::SurfaceDestroyed), + }, + { + .name = "nativeSetViewportMetrics", + .signature = "(JFIIIIIIIIII)V", + .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), + }, + { + .name = "nativeDispatchPointerDataPacket", + .signature = "(JLjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchPointerDataPacket), + }, + { + .name = "nativeDispatchSemanticsAction", + .signature = "(JIILjava/nio/ByteBuffer;I)V", + .fnPtr = reinterpret_cast(&shell::DispatchSemanticsAction), + }, + { + .name = "nativeSetSemanticsEnabled", + .signature = "(JZ)V", + .fnPtr = reinterpret_cast(&shell::SetSemanticsEnabled), + }, + { + .name = "nativeSetAccessibilityFeatures", + .signature = "(JI)V", + .fnPtr = reinterpret_cast(&shell::SetAccessibilityFeatures), + }, + { + .name = "nativeGetIsSoftwareRenderingEnabled", + .signature = "()Z", + .fnPtr = reinterpret_cast(&shell::GetIsSoftwareRendering), + }, + { + .name = "nativeRegisterTexture", + .signature = "(JJLandroid/graphics/SurfaceTexture;)V", + .fnPtr = reinterpret_cast(&shell::RegisterTexture), + }, + { + .name = "nativeMarkTextureFrameAvailable", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::MarkTextureFrameAvailable), + }, + { + .name = "nativeUnregisterTexture", + .signature = "(JJ)V", + .fnPtr = reinterpret_cast(&shell::UnregisterTexture), + }, }; - if (env->RegisterNatives(g_flutter_jni_class->obj(), - flutter_jni_methods, - arraysize(flutter_jni_methods)) != 0) { + if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, + arraysize(flutter_jni_methods)) != 0) { FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI"; return false; } g_handle_platform_message_method = - env->GetMethodID(g_flutter_engine_class->obj(), - "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + env->GetMethodID(g_flutter_engine_class->obj(), "handlePlatformMessage", + "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; return false; } - g_handle_platform_message_response_method = - env->GetMethodID(g_flutter_engine_class->obj(), - "handlePlatformMessageResponse", "(I[B)V"); + g_handle_platform_message_response_method = env->GetMethodID( + g_flutter_engine_class->obj(), "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; @@ -949,20 +954,21 @@ bool RegisterNewApi(JNIEnv* env) { "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { - FML_LOG(ERROR) << "Could not locate updateCustomAccessibilityActions method"; + FML_LOG(ERROR) + << "Could not locate updateCustomAccessibilityActions method"; return false; } - g_on_first_frame_method = env->GetMethodID(g_flutter_engine_class->obj(), - "onFirstFrame", "()V"); + g_on_first_frame_method = + env->GetMethodID(g_flutter_engine_class->obj(), "onFirstFrame", "()V"); if (g_on_first_frame_method == nullptr) { FML_LOG(ERROR) << "Could not locate onFirstFrame method"; return false; } - g_on_engine_restart_method = env->GetMethodID( - g_flutter_engine_class->obj(), "onPreEngineRestart", "()V"); + g_on_engine_restart_method = env->GetMethodID(g_flutter_engine_class->obj(), + "onPreEngineRestart", "()V"); if (g_on_engine_restart_method == nullptr) { FML_LOG(ERROR) << "Could not locate onEngineRestart method"; @@ -994,7 +1000,9 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { return false; } - // TODO(mattcarroll): this assumes the use of the new API is based on what we're compiling against but that's not true. Need to support both simultaneously. + // TODO(mattcarroll): this assumes the use of the new API is based on what + // we're compiling against but that's not true. Need to support both + // simultaneously. bool is_using_new_api = false; g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass("io/flutter/embedding/FlutterJNI")); @@ -1005,7 +1013,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { } g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("android/graphics/SurfaceTexture")); + env, env->FindClass("android/graphics/SurfaceTexture")); if (g_surface_texture_class->is_null()) { FML_LOG(ERROR) << "Could not locate SurfaceTexture class"; return false; From 05cdad763cb4cf15e131df3cc86c87a76c691301 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Fri, 7 Sep 2018 17:12:51 -0700 Subject: [PATCH 09/11] Update licenses file. --- ci/licenses_golden/licenses_flutter | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 67ad0c9916366..8d01c9fa53b7a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -93,7 +93,6 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDele FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/legacy/FlutterNativeView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java @@ -202,6 +201,9 @@ FILE: ../../../flutter/lib/ui/natives.dart FILE: ../../../flutter/lib/ui/window/window.h FILE: ../../../flutter/shell/common/skia_event_tracer_impl.cc FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterEngine.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterJNI.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/FlutterRenderer.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONUtil.java FILE: ../../../flutter/shell/platform/darwin/desktop/Info.plist FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec From d2c08086272950b26005ad5ce6051a0ab2d80bc3 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 18 Sep 2018 16:47:54 -0700 Subject: [PATCH 10/11] Exposed FlutterEngine from plugin system. Updated some access modifiers as needed. --- .../android/io/flutter/embedding/FlutterActivity.java | 6 ++++++ .../android/io/flutter/embedding/FlutterEngine.java | 2 +- .../android/io/flutter/embedding/FlutterFragment.java | 10 ++++++++++ .../android/io/flutter/embedding/FlutterRenderer.java | 1 + .../android/io/flutter/embedding/FlutterView.java | 2 +- .../embedding/legacy/FlutterPluginRegistry.java | 5 +++++ .../io/flutter/embedding/legacy/PluginRegistry.java | 4 +++- 7 files changed, 27 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/FlutterActivity.java index 0f40e27e83458..61e8ea79708fa 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/FlutterActivity.java @@ -22,6 +22,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; +import io.flutter.embedding.legacy.PluginRegistry; import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.view.FlutterMain; @@ -106,6 +107,11 @@ public void onUserLeaveHint() { flutterFragment.onUserLeaveHint(); } + @Nullable + protected FlutterEngine getFlutterEngine() { + return flutterFragment.getFlutterEngine(); + } + /** * Sets up a {@code FrameLayout} that takes up all available space in the {@code Activity}. This * {@code FrameLayout} will hold a {@code FlutterFragment}, which displays a {@code FlutterView}. diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java index f7cfb995aa97c..6179a48e37765 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -46,7 +46,7 @@ public class FlutterEngine implements BinaryMessenger { private boolean applicationIsRunning; private int mNextReplyId = 1; - FlutterEngine( + public FlutterEngine( Context context, Resources resources, boolean isBackgroundView diff --git a/shell/platform/android/io/flutter/embedding/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/FlutterFragment.java index 62e1b2e023f8d..ea8f3a4d76491 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/FlutterFragment.java @@ -297,6 +297,16 @@ protected void onFlutterViewCreated(@SuppressWarnings("unused") @NonNull Flutter // no-op } + /** + * The {@link FlutterEngine} that backs the Flutter content presented by this {@code Fragment}. + * + * @return the {@link FlutterEngine} held by this {@code Fragment} + */ + @Nullable + protected FlutterEngine getFlutterEngine() { + return flutterEngine; + } + /** * Creates a {@link View} containing the same {@link Drawable} as the one set as the * {@code windowBackground} of the parent activity for use as a launch splash view. diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java index 53a5f5ef272f6..7167c891f02f1 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -72,6 +72,7 @@ private void notifyFirstFrameListeners() { // TODO(mattcarroll): this method probably shouldn't be public. It's part of an obscure relationship // with PlatformViewsController. Re-evaluate that relationship and see if we can get rid of // PlatformViewsController. + // However, I did find that this method is called from the Camera plugin. // TODO(mattcarroll): detachFromGLContext requires API 16. what should we do for earlier APIs? @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index 6a03ba5833993..e48963783f4ff 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -626,7 +626,7 @@ void ensureAccessibilityEnabled() { mAccessibilityNodeProvider.setAccessibilityEnabled(true); } - void resetAccessibilityTree() { + public void resetAccessibilityTree() { if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.reset(); } diff --git a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java index 46032e2e023a2..37def30554432 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/FlutterPluginRegistry.java @@ -90,6 +90,11 @@ private class FlutterRegistrar implements Registrar { this.pluginKey = pluginKey; } + @Override + public FlutterEngine getFlutterEngine() { + return flutterEngine; + } + @Override public Activity activity() { return mActivity; diff --git a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java index 23d2617bdf2ad..2d315a884b544 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PluginRegistry.java @@ -10,7 +10,6 @@ import io.flutter.embedding.FlutterEngine; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.embedding.FlutterView; import io.flutter.view.TextureRegistry; /** @@ -62,6 +61,9 @@ public interface PluginRegistry { * Receiver of registrations from a single plugin. */ interface Registrar { + // TODO(mattcarroll): Added because plugins need to requisition SurfaceTextures + FlutterEngine getFlutterEngine(); + /** * Returns the {@link Activity} that forms the plugin's operating context. * From bd9c5e6a4f9bf675226261e925613b9b20418ff9 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Tue, 18 Sep 2018 18:07:45 -0700 Subject: [PATCH 11/11] PR Updates. --- .../android/io/flutter/embedding/FlutterEngine.java | 6 +++--- shell/platform/android/io/flutter/embedding/FlutterJNI.java | 4 +--- .../android/io/flutter/embedding/FlutterRenderer.java | 4 ++-- .../platform/android/io/flutter/embedding/FlutterView.java | 2 +- .../flutter/embedding/legacy/PlatformViewsController.java | 2 +- shell/platform/android/platform_view_android_jni.cc | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/FlutterEngine.java index 6179a48e37765..dce292bc1225c 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/FlutterEngine.java @@ -23,7 +23,7 @@ * back to UI interaction. * * To start running Flutter within this {@code FlutterEngine}, use {@link #runFromBundle(FlutterRunArguments)}. - * The {@link #runFromBundle(FlutterRunArguments)} method may not be invoked twice on the same + * The {@link #runFromBundle(FlutterRunArguments)} method must not be invoked twice on the same * {@code FlutterEngine}. * * To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a @@ -146,8 +146,8 @@ public void attachViewAndActivity(Activity activity) { pluginRegistry.attach(this, activity); } - // TODO(mattcarroll): This method says it's unused. Is that true? Do we need it? - public String getObservatoryUri() { + // TODO(mattcarroll): Implement observatory lookup for "flutter attach" + public String getObservatoryUrl() { return flutterJNI.nativeGetObservatoryUri(); } diff --git a/shell/platform/android/io/flutter/embedding/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/FlutterJNI.java index 62aea0e729c65..ef083e10dfe1e 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/FlutterJNI.java @@ -9,9 +9,7 @@ public class FlutterJNI { //----- Start from FlutterView ----- - public native void nativeSurfaceCreated(long nativePlatformViewAndroid, - Surface surface, - int backgroundColor); + public native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); public native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, diff --git a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java index 7167c891f02f1..0b17a6f8392ce 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/FlutterRenderer.java @@ -126,8 +126,8 @@ public void release() { //------ END TextureRegistry IMPLEMENTATION ---- // TODO(mattcarroll): what exactly is this method intended to do? - public void surfaceCreated(Surface surface, int backgroundColor) { - flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface, backgroundColor); + public void surfaceCreated(Surface surface) { + flutterJNI.nativeSurfaceCreated(nativeObjectReference, surface); } // TODO(mattcarroll): what exactly is this method intended to do? diff --git a/shell/platform/android/io/flutter/embedding/FlutterView.java b/shell/platform/android/io/flutter/embedding/FlutterView.java index e48963783f4ff..7aae7c0c707de 100644 --- a/shell/platform/android/io/flutter/embedding/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/FlutterView.java @@ -147,7 +147,7 @@ static final class ViewportMetrics { @Override public void surfaceCreated(SurfaceHolder holder) { assertAttachedToFlutterRenderer(); - flutterRenderer.surfaceCreated(holder.getSurface(), backgroundColor); + flutterRenderer.surfaceCreated(holder.getSurface()); } @Override diff --git a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java index 190a17c1d91b5..4bbb80236c75b 100644 --- a/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/embedding/legacy/PlatformViewsController.java @@ -57,7 +57,7 @@ public void attachFlutterEngine(FlutterEngine flutterEngine, Context context) { if (this.flutterEngine != null) throw new AssertionError( "A PlatformViewsController can only be attached to a single FlutterEngine.\n" + - "attachFlutterEngine was called while a FlutterView was already attached." + "attachFlutterEngine was called while a FlutterEngine was already attached." ); this.flutterEngine = flutterEngine; this.context = context; diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 15760ac89a9dc..5d4e06d18ad2d 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -853,7 +853,7 @@ bool RegisterNewApi(JNIEnv* env) { }, { .name = "nativeSurfaceCreated", - .signature = "(JLandroid/view/Surface;I)V", + .signature = "(JLandroid/view/Surface;)V", .fnPtr = reinterpret_cast(&shell::SurfaceCreated), }, {