From 06485f3624df1ec75a20a9ccf8024f8e60d7c582 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Wed, 12 Jun 2019 14:57:13 -0700 Subject: [PATCH 1/6] Android Embedding Refactor PR 31: Integrated PlatformViewsController in the new embedding. --- .../embedding/android/FlutterActivity.java | 25 ++++++- .../embedding/android/FlutterFragment.java | 71 ++++++++++++++++--- .../embedding/android/FlutterView.java | 64 ++++++++++++----- .../embedding/engine/FlutterEngine.java | 3 + .../engine/FlutterEnginePluginRegistry.java | 69 +++++++++--------- .../activity/ActivityControlSurface.java | 14 +++- .../activity/ActivityPluginBinding.java | 8 +++ .../plugins/shim/ShimPluginRegistry.java | 38 +--------- .../engine/plugins/shim/ShimRegistrar.java | 2 +- .../platform/PlatformViewRegistryImpl.java | 4 ++ .../platform/PlatformViewsController.java | 2 +- 11 files changed, 196 insertions(+), 104 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 3341db91c7569..3e04584805ab3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -16,6 +16,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.TypedValue; @@ -66,7 +67,10 @@ * {@code Fragment}. */ // TODO(mattcarroll): explain each call forwarded to Fragment (first requires resolution of PluginRegistry API). -public class FlutterActivity extends FragmentActivity implements OnFirstFrameRenderedListener { +public class FlutterActivity extends FragmentActivity + implements FlutterFragment.FlutterEngineProvider, + FlutterFragment.FlutterEngineConfigurator, + OnFirstFrameRenderedListener { private static final String TAG = "FlutterActivity"; // Meta-data arguments, processed from manifest XML. @@ -365,6 +369,25 @@ protected boolean shouldAttachEngineToActivity() { return true; } + /** + * Hook for subclasses to easily provide a custom {@code FlutterEngine}. + */ + @Nullable + @Override + public FlutterEngine provideFlutterEngine(@NonNull Context context) { + // No-op. Hook for subclasses. + return null; + } + + /** + * Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register + * plugins. + */ + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + // No-op. Hook for subclasses. + } + @Override public void onPostResume() { super.onPostResume(); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 59d9719dc940d..cecbfb211fa71 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -6,6 +6,8 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; +import android.app.Activity; +import android.arch.lifecycle.Lifecycle; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -27,6 +29,7 @@ import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; import io.flutter.plugin.platform.PlatformPlugin; +import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.view.FlutterMain; /** @@ -296,6 +299,8 @@ public T build() { private FlutterView flutterView; @Nullable private PlatformPlugin platformPlugin; + @NonNull + private final PlatformViewsController platformViewsController = new PlatformViewsController(); private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() { @Override @@ -359,8 +364,14 @@ public void onAttach(@NonNull Context context) { // sync with the Activity. We use the Fragment's Lifecycle because it is possible that the // attached Activity is not a LifecycleOwner. Log.d(TAG, "Attaching FlutterEngine to the Activity that owns this Fragment."); - flutterEngine.getActivityControlSurface().attachToActivity(getActivity(), getLifecycle()); + flutterEngine.getActivityControlSurface().attachToActivity( + getActivity(), + getLifecycle(), + platformViewsController + ); } + + configureFlutterEngine(flutterEngine); } private void initializeFlutter(@NonNull Context context) { @@ -401,11 +412,11 @@ private void setupFlutterEngine() { // Defer to the Activity that owns us to provide a FlutterEngine. Log.d(TAG, "Deferring to attached Activity to provide a FlutterEngine."); FlutterEngineProvider flutterEngineProvider = (FlutterEngineProvider) attachedActivity; - flutterEngine = flutterEngineProvider.getFlutterEngine(getContext()); + flutterEngine = flutterEngineProvider.provideFlutterEngine(getContext()); if (flutterEngine != null) { isFlutterEngineFromActivity = true; + return; } - return; } // Neither our subclass, nor our owning Activity wanted to provide a custom FlutterEngine. @@ -434,11 +445,34 @@ protected FlutterEngine createFlutterEngine(@NonNull Context context) { return null; } + /** + * Configures a {@link FlutterEngine} after its creation. + *

+ * This method is called after the given {@link FlutterEngine} has been attached to the + * owning {@code FragmentActivity}. See + * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle, PlatformViewsController)}. + *

+ * It is possible that the owning {@code FragmentActivity} opted not to connect itself as + * an {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that + * case, any configuration, e.g., plugins, must not expect or depend upon an available + * {@code Activity} at the time that this method is invoked. + *

+ * The default behavior of this method is to defer to the owning {@code FragmentActivity} + * as a {@link FlutterEngineConfigurator}. Subclasses can override this method if the + * subclass needs to override the {@code FragmentActivity}'s behavior, or add to it. + */ + protected void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + FragmentActivity attachedActivity = getActivity(); + if (attachedActivity instanceof FlutterEngineConfigurator) { + ((FlutterEngineConfigurator) attachedActivity).configureFlutterEngine(flutterEngine); + } + } + @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.v(TAG, "Creating FlutterView."); - flutterView = new FlutterView(getContext(), getRenderMode(), getTransparencyMode()); + flutterView = new FlutterView(getActivity(), getRenderMode(), getTransparencyMode()); flutterView.addOnFirstFrameRenderedListener(onFirstFrameRenderedListener); return flutterView; } @@ -546,7 +580,7 @@ public void onStart() { @Override public void run() { Log.v(TAG, "Attaching FlutterEngine to FlutterView."); - flutterView.attachToFlutterEngine(flutterEngine); + flutterView.attachToFlutterEngine(flutterEngine, platformViewsController); // TODO(mattcarroll): the following call should exist here, but the plugin system needs to be revamped. // The existing attach() method does not know how to handle this kind of FlutterView. @@ -788,7 +822,7 @@ protected void onFirstFrameRendered() {} * {@link FlutterActivity}s and/or {@code FlutterFragments}. *

* If the {@link FragmentActivity} that owns this {@code FlutterFragment} implements - * {@code FlutterEngineProvider}, that {@link FlutterActivity} will be given an opportunity + * {@code FlutterEngineProvider}, that {@link FragmentActivity} will be given an opportunity * to provide a {@link FlutterEngine} instead of the {@code FlutterFragment} creating a * new one. The {@link FragmentActivity} can provide an existing, pre-warmed {@link FlutterEngine}, * if desired. @@ -804,6 +838,27 @@ public interface FlutterEngineProvider { * to provide its own {@code FlutterEngine} instance. */ @Nullable - FlutterEngine getFlutterEngine(@NonNull Context context); + FlutterEngine provideFlutterEngine(@NonNull Context context); + } + + /** + * Configures a {@link FlutterEngine} after it is created, e.g., adds plugins. + *

+ * This interface may be applied to a {@link FragmentActivity} that owns a {@code FlutterFragment}. + */ + public interface FlutterEngineConfigurator { + /** + * Configures the given {@link FlutterEngine}. + *

+ * This method is called after the given {@link FlutterEngine} has been attached to the + * owning {@code FragmentActivity}. See + * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle, PlatformViewsController)}. + *

+ * It is possible that the owning {@code FragmentActivity} opted not to connect itself as + * an {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that + * case, any configuration, e.g., plugins, must not expect or depend upon an available + * {@code Activity} at the time that this method is invoked. + */ + void configureFlutterEngine(@NonNull FlutterEngine flutterEngine); } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 45ad7515effed..3f3e74d7f18f3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -5,6 +5,7 @@ package io.flutter.embedding.android; import android.annotation.TargetApi; +import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -36,6 +37,7 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; import io.flutter.plugin.editing.TextInputPlugin; +import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.view.AccessibilityBridge; /** @@ -72,6 +74,8 @@ public class FlutterView extends FrameLayout { @Nullable private FlutterRenderer.RenderSurface renderSurface; private boolean didRenderFirstFrame; + @Nullable + private PlatformViewsController platformViewsController; // Connections to a Flutter execution context. @Nullable @@ -117,9 +121,11 @@ public void onFirstFrameRendered() { *

  • {@link #renderMode} defaults to {@link RenderMode#surface}.
  • *
  • {@link #transparencyMode} defaults to {@link TransparencyMode#opaque}.
  • * + * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} + * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context) { - this(context, null, null, null); + public FlutterView(@NonNull Activity activity) { + this(activity, null, null, null); } /** @@ -127,38 +133,49 @@ public FlutterView(@NonNull Context context) { * and allows selection of a {@link #renderMode}. *

    * {@link #transparencyMode} defaults to {@link TransparencyMode#opaque}. + *

    + * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} + * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) { - this(context, null, renderMode, null); + public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode) { + this(activity, null, renderMode, null); } /** * Constructs a {@code FlutterView} programmatically, without any XML attributes, * assumes the use of {@link RenderMode#surface}, and allows selection of a {@link #transparencyMode}. + *

    + * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} + * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @NonNull TransparencyMode transparencyMode) { - this(context, null, RenderMode.surface, transparencyMode); + public FlutterView(@NonNull Activity activity, @NonNull TransparencyMode transparencyMode) { + this(activity, null, RenderMode.surface, transparencyMode); } /** * Constructs a {@code FlutterView} programmatically, without any XML attributes, and allows * a selection of {@link #renderMode} and {@link #transparencyMode}. + *

    + * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} + * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode, @NonNull TransparencyMode transparencyMode) { - this(context, null, renderMode, transparencyMode); + public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode, @NonNull TransparencyMode transparencyMode) { + this(activity, null, renderMode, transparencyMode); } /** * Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner. - * - * // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr + *

    + * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} + * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, null, null); + // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr + public FlutterView(@NonNull Activity activity, @Nullable AttributeSet attrs) { + this(activity, attrs, null, null); } - private FlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode, @Nullable TransparencyMode transparencyMode) { - super(context, attrs); + private FlutterView(@NonNull Activity activity, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode, @Nullable TransparencyMode transparencyMode) { + super(activity, attrs); this.renderMode = renderMode == null ? RenderMode.surface : renderMode; this.transparencyMode = transparencyMode != null ? transparencyMode : TransparencyMode.opaque; @@ -511,7 +528,10 @@ private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExp * See {@link #detachFromFlutterEngine()} for information on how to detach from a * {@link FlutterEngine}. */ - public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { + public void attachToFlutterEngine( + @NonNull FlutterEngine flutterEngine, + @NonNull PlatformViewsController platformViewsController + ) { Log.d(TAG, "Attaching to a FlutterEngine: " + flutterEngine); if (isAttachedToFlutterEngine()) { if (flutterEngine == this.flutterEngine) { @@ -527,6 +547,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { } this.flutterEngine = flutterEngine; + this.platformViewsController = platformViewsController; // Instruct our FlutterRenderer that we are now its designated RenderSurface. didRenderFirstFrame = false; @@ -537,7 +558,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { textInputPlugin = new TextInputPlugin( this, this.flutterEngine.getDartExecutor(), - null + platformViewsController ); androidKeyProcessor = new AndroidKeyProcessor( this.flutterEngine.getKeyEventChannel(), @@ -549,9 +570,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { flutterEngine.getAccessibilityChannel(), (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE), getContext().getContentResolver(), - // TODO(mattcaroll): plumb the platform views controller to the accessibility bridge. - // https://github.com/flutter/flutter/issues/29618 - null + platformViewsController ); accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener); resetWillNotDraw( @@ -559,6 +578,10 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { accessibilityBridge.isTouchExplorationEnabled() ); + // Connect AccessibilityBridge to the PlatformViewsController within the FlutterEngine. + // This allows platform Views to hook into Flutter's overall accessibility system. + platformViewsController.attachAccessibilityBridge(accessibilityBridge); + // Inform the Android framework that it should retrieve a new InputConnection // now that an engine is attached. // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin @@ -597,6 +620,9 @@ public void detachFromFlutterEngine() { listener.onFlutterEngineDetachedFromFlutterView(); } + // Disconnect the FlutterEngine's PlatformViewsController from the AccessibilityBridge. + platformViewsController.detachAccessibiltyBridge(); + // Disconnect and clean up the AccessibilityBridge. accessibilityBridge.release(); accessibilityBridge = null; diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 08ed9f75004f7..f79224247f8af 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -29,6 +29,8 @@ import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.embedding.engine.systemchannels.SystemChannel; import io.flutter.embedding.engine.systemchannels.TextInputChannel; +import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate; +import io.flutter.plugin.platform.PlatformViewsController; /** * A single Flutter execution environment. @@ -139,6 +141,7 @@ public FlutterEngine(@NonNull Context context) { textInputChannel = new TextInputChannel(dartExecutor); androidLifecycle = new FlutterEngineAndroidLifecycle(this); + this.pluginRegistry = new FlutterEnginePluginRegistry( context.getApplicationContext(), this, diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java index 370ac44605e49..82b89cbf52b3a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java @@ -34,13 +34,14 @@ import io.flutter.embedding.engine.plugins.service.ServiceAware; import io.flutter.embedding.engine.plugins.service.ServiceControlSurface; import io.flutter.embedding.engine.plugins.service.ServicePluginBinding; +import io.flutter.plugin.platform.PlatformViewsController; class FlutterEnginePluginRegistry implements PluginRegistry, ActivityControlSurface, ServiceControlSurface, BroadcastReceiverControlSurface, ContentProviderControlSurface { - private static final String TAG = "EnginePluginRegistry"; + private static final String TAG = "FlutterEnginePluginRegistry"; // PluginRegistry @NonNull @@ -277,16 +278,32 @@ private boolean isAttachedToActivity() { } @Override - public void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle) { + public void attachToActivity( + @NonNull Activity activity, + @NonNull Lifecycle lifecycle, + @NonNull PlatformViewsController platformViewsController + ) { Log.v(TAG, "Attaching to an Activity: " + activity + "." + (isWaitingForActivityReattachment ? " This is after a config change." : "")); // If we were already attached to an Android component, detach from it. detachFromAndroidComponent(); this.activity = activity; - this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); + this.activityPluginBinding = new FlutterEngineActivityPluginBinding( + activity, + platformViewsController + ); this.flutterEngineAndroidLifecycle.setBackingLifecycle(lifecycle); + // Activate the PlatformViewsController. This must happen before any plugins attempt + // to use it, otherwise an error strack trace will appear that says there is no + // flutter/platform_views channel. + platformViewsController.attach( + activity, + pluginBinding.getFlutterEngine().getRenderer(), + pluginBinding.getFlutterEngine().getDartExecutor() + ); + // Notify all ActivityAware plugins that they are now attached to a new Activity. for (ActivityAware activityAware : activityAwarePlugins.values()) { if (isWaitingForActivityReattachment) { @@ -308,6 +325,9 @@ public void detachFromActivityForConfigChanges() { activityAware.onDetachedFromActivityForConfigChanges(); } + // Deactivate PlatformViewsController. + activityPluginBinding.platformViewsController.detach(); + flutterEngineAndroidLifecycle.setBackingLifecycle(null); activity = null; activityPluginBinding = null; @@ -504,6 +524,8 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi @NonNull private final Activity activity; @NonNull + private final PlatformViewsController platformViewsController; + @NonNull private final Set onRequestPermissionsResultListeners = new HashSet<>(); @NonNull private final Set onActivityResultListeners = new HashSet<>(); @@ -512,32 +534,28 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi @NonNull private final Set onUserLeaveHintListeners = new HashSet<>(); - public FlutterEngineActivityPluginBinding(@NonNull Activity activity) { + public FlutterEngineActivityPluginBinding(@NonNull Activity activity, @NonNull PlatformViewsController platformViewsController) { this.activity = activity; + this.platformViewsController = platformViewsController; } - /** - * Returns the {@link Activity} that is currently attached to the {@link FlutterEngine} that - * owns this {@code ActivityPluginBinding}. - */ @Override @NonNull public Activity getActivity() { return activity; } - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onRequestPermissionsResult(...)} method is invoked. - */ + @NonNull + @Override + public PlatformViewsController getPlatformViewsController() { + return platformViewsController; + } + @Override public void addRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) { onRequestPermissionsResultListeners.add(listener); } - /** - * Removes a listener that was added in {@link #addRequestPermissionsResultListener(io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener)}. - */ @Override public void removeRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) { onRequestPermissionsResultListeners.remove(listener); @@ -555,18 +573,11 @@ boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permission return didConsumeResult; } - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onActivityResult(...)} method is invoked. - */ @Override public void addActivityResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener) { onActivityResultListeners.add(listener); } - /** - * Removes a listener that was added in {@link #addActivityResultListener(io.flutter.plugin.common.PluginRegistry.ActivityResultListener)}. - */ @Override public void removeActivityResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener) { onActivityResultListeners.remove(listener); @@ -584,18 +595,11 @@ boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) return didConsumeResult; } - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onNewIntent(...)} method is invoked. - */ @Override public void addOnNewIntentListener(@NonNull io.flutter.plugin.common.PluginRegistry.NewIntentListener listener) { onNewIntentListeners.add(listener); } - /** - * Removes a listener that was added in {@link #addOnNewIntentListener(io.flutter.plugin.common.PluginRegistry.NewIntentListener)}. - */ @Override public void removeOnNewIntentListener(@NonNull io.flutter.plugin.common.PluginRegistry.NewIntentListener listener) { onNewIntentListeners.remove(listener); @@ -611,18 +615,11 @@ void onNewIntent(@Nullable Intent intent) { } } - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onUserLeaveHint()} method is invoked. - */ @Override public void addOnUserLeaveHintListener(@NonNull io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener) { onUserLeaveHintListeners.add(listener); } - /** - * Removes a listener that was added in {@link #addOnUserLeaveHintListener(io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener)}. - */ @Override public void removeOnUserLeaveHintListener(@NonNull io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener) { onUserLeaveHintListeners.remove(listener); diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java index c5833eca7ce67..19ec3d1354271 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java @@ -10,6 +10,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import io.flutter.plugin.platform.PlatformViewsController; + /** * Control surface through which an {@link Activity} attaches to a {@link FlutterEngine}. *

    @@ -46,8 +48,11 @@ public interface ActivityControlSurface { * executing Dart code, the {@link Activity} should invoke this method. At that point the * {@link FlutterEngine} is considered "attached" to the {@link Activity} and all * {@link ActivityAware} plugins are given access to the {@link Activity}. + *

    + * Flutter expects that platform views are supported whenever a Flutter UI is available, + * therefore, a {@link PlatformViewsController} is required when attaching to an {@code Activity}. */ - void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle); + void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle, @NonNull PlatformViewsController platformViewsController); /** * Call this method from the {@link Activity} that is attached to this {@code ActivityControlSurfaces}'s @@ -56,7 +61,11 @@ public interface ActivityControlSurface { *

    * This method gives each {@link ActivityAware} plugin an opportunity to clean up its references * before the {@link Activity is destroyed}. + *

    + * This method does NOT detach the {@link PlatformViewsController} that was provided in the + * {@link #attachToActivity(Activity, Lifecycle, PlatformViewsController)} method. */ + // TODO(mattcarroll): once platform views are fully updated for lifecycle purposes, reconsider detaching PlatformViewsController here. void detachFromActivityForConfigChanges(); /** @@ -66,6 +75,9 @@ public interface ActivityControlSurface { *

    * This method gives each {@link ActivityAware} plugin an opportunity to clean up its references * before the {@link Activity is destroyed}. + *

    + * Invocation of this method also detaches the {@link PlatformViewsController} that was provided + * in the {@link #attachToActivity(Activity, Lifecycle, PlatformViewsController)} method. */ void detachFromActivity(); diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java index 9420c43c57b10..c9b0df3e2338c 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java @@ -8,6 +8,7 @@ import android.support.annotation.NonNull; import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.platform.PlatformViewsController; /** * Binding that gives {@link ActivityAware} plugins access to an associated {@link Activity} and @@ -22,6 +23,13 @@ public interface ActivityPluginBinding { @NonNull Activity getActivity(); + /** + * Returns the {@link PlatformViewsController} that is currently connected to the Flutter UI + * being displayed in the given {@code Activity}. + */ + @NonNull + PlatformViewsController getPlatformViewsController(); + /** * Adds a listener that is invoked whenever the associated {@link Activity}'s * {@code onRequestPermissionsResult(...)} method is invoked. diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java index ce85eb0f37c90..011eea303bab1 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java @@ -4,7 +4,6 @@ package io.flutter.embedding.engine.plugins.shim; -import android.app.Activity; import android.support.annotation.NonNull; import java.util.HashMap; @@ -18,8 +17,6 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.platform.PlatformViewsController; -import io.flutter.view.FlutterView; /** * A {@link PluginRegistry} that is shimmed to use the new Android embedding and plugin API behind @@ -41,24 +38,11 @@ public class ShimPluginRegistry implements PluginRegistry { private static final String TAG = "ShimPluginRegistry"; private final FlutterEngine flutterEngine; - private final PlatformViewsController platformViewsController; private final Map pluginMap = new HashMap<>(); private final ShimRegistrarAggregate shimRegistrarAggregate; - private final FlutterEngine.EngineLifecycleListener engineLifecycleListener = new FlutterEngine.EngineLifecycleListener() { - @Override - public void onPreEngineRestart() { - Log.v(TAG, "onPreEngineRestart()"); - ShimPluginRegistry.this.onPreEngineRestart(); - } - }; - public ShimPluginRegistry( - @NonNull FlutterEngine flutterEngine, - @NonNull PlatformViewsController platformViewsController - ) { + public ShimPluginRegistry(@NonNull FlutterEngine flutterEngine) { this.flutterEngine = flutterEngine; - this.flutterEngine.addEngineLifecycleListener(engineLifecycleListener); - this.platformViewsController = platformViewsController; this.shimRegistrarAggregate = new ShimRegistrarAggregate(); this.flutterEngine.getPlugins().add(shimRegistrarAggregate); } @@ -86,26 +70,6 @@ public T valuePublishedByPlugin(String pluginKey) { return (T) pluginMap.get(pluginKey); } - //----- From FlutterPluginRegistry that aren't in the PluginRegistry interface ----// - public void attach(FlutterView flutterView, Activity activity) { - Log.v(TAG, "Attaching to a FlutterView and an Activity."); - platformViewsController.attach(activity, flutterEngine.getRenderer(), flutterEngine.getDartExecutor()); - } - - public void detach() { - Log.v(TAG, "Detaching from a FlutterView and an Activity."); - platformViewsController.detach(); - platformViewsController.onFlutterViewDestroyed(); - } - - private void onPreEngineRestart() { - platformViewsController.onPreEngineRestart(); - } - - public PlatformViewsController getPlatformViewsController() { - return platformViewsController; - } - /** * Aggregates all {@link ShimRegistrar}s within one single {@link FlutterPlugin}. *

    diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java index e6bb8e50c7860..813353275b3af 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java @@ -74,7 +74,7 @@ public TextureRegistry textures() { @Override public PlatformViewRegistry platformViewRegistry() { - return null; + return activityPluginBinding != null ? activityPluginBinding.getPlatformViewsController().getRegistry() : null; } @Override diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java index 5c303034c764f..87ffcfc001abe 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java @@ -7,6 +7,8 @@ import java.util.HashMap; import java.util.Map; +import io.flutter.Log; + class PlatformViewRegistryImpl implements PlatformViewRegistry { PlatformViewRegistryImpl() { @@ -20,11 +22,13 @@ class PlatformViewRegistryImpl implements PlatformViewRegistry { public boolean registerViewFactory(String viewTypeId, PlatformViewFactory factory) { if (viewFactories.containsKey(viewTypeId)) return false; + Log.d("REPRO", "Registering " + viewTypeId + " -> " + factory + ", this: " + this); viewFactories.put(viewTypeId, factory); return true; } PlatformViewFactory getFactory(String viewTypeId) { + Log.d("REPRO", "Being asked for factory for " + viewTypeId + "... -> " + viewFactories.get(viewTypeId) + ", this: " + this); return viewFactories.get(viewTypeId); } } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 201cfcb631066..d0c0bfbd1392d 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -263,7 +263,7 @@ public PlatformViewsController() { * Attaches this platform views controller to its input and output channels. * * @param context The base context that will be passed to embedded views created by this controller. - * This should be the context of the Activity hosting the Flutter application. + * This should be the {@code Application} {@code Context}. * @param textureRegistry The texture registry which provides the output textures into which the embedded views * will be rendered. * @param dartExecutor The dart execution context, which is used to setup a system channel. From 94e260d90ffd0ff77fc8536fdb7eeaad9128769e Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 13 Jun 2019 23:43:56 -0700 Subject: [PATCH 2/6] PR Updates. --- .../android/io/flutter/embedding/engine/FlutterEngine.java | 3 --- .../io/flutter/plugin/platform/PlatformViewRegistryImpl.java | 4 ---- .../io/flutter/plugin/platform/PlatformViewsController.java | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index f79224247f8af..08ed9f75004f7 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -29,8 +29,6 @@ import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.embedding.engine.systemchannels.SystemChannel; import io.flutter.embedding.engine.systemchannels.TextInputChannel; -import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate; -import io.flutter.plugin.platform.PlatformViewsController; /** * A single Flutter execution environment. @@ -141,7 +139,6 @@ public FlutterEngine(@NonNull Context context) { textInputChannel = new TextInputChannel(dartExecutor); androidLifecycle = new FlutterEngineAndroidLifecycle(this); - this.pluginRegistry = new FlutterEnginePluginRegistry( context.getApplicationContext(), this, diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java index 87ffcfc001abe..5c303034c764f 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java @@ -7,8 +7,6 @@ import java.util.HashMap; import java.util.Map; -import io.flutter.Log; - class PlatformViewRegistryImpl implements PlatformViewRegistry { PlatformViewRegistryImpl() { @@ -22,13 +20,11 @@ class PlatformViewRegistryImpl implements PlatformViewRegistry { public boolean registerViewFactory(String viewTypeId, PlatformViewFactory factory) { if (viewFactories.containsKey(viewTypeId)) return false; - Log.d("REPRO", "Registering " + viewTypeId + " -> " + factory + ", this: " + this); viewFactories.put(viewTypeId, factory); return true; } PlatformViewFactory getFactory(String viewTypeId) { - Log.d("REPRO", "Being asked for factory for " + viewTypeId + "... -> " + viewFactories.get(viewTypeId) + ", this: " + this); return viewFactories.get(viewTypeId); } } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index d0c0bfbd1392d..201cfcb631066 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -263,7 +263,7 @@ public PlatformViewsController() { * Attaches this platform views controller to its input and output channels. * * @param context The base context that will be passed to embedded views created by this controller. - * This should be the {@code Application} {@code Context}. + * This should be the context of the Activity hosting the Flutter application. * @param textureRegistry The texture registry which provides the output textures into which the embedded views * will be rendered. * @param dartExecutor The dart execution context, which is used to setup a system channel. From 64219fa7ee9acece22854524d511da1b30c57c64 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Thu, 13 Jun 2019 23:51:52 -0700 Subject: [PATCH 3/6] PR Updates. --- .../embedding/android/FlutterView.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 3f3e74d7f18f3..38608a25d749a 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -124,8 +124,8 @@ public void onFirstFrameRendered() { * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Activity activity) { - this(activity, null, null, null); + public FlutterView(@NonNull Context context) { + this(context, null, null, null); } /** @@ -137,8 +137,8 @@ public FlutterView(@NonNull Activity activity) { * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode) { - this(activity, null, renderMode, null); + public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) { + this(context, null, renderMode, null); } /** @@ -148,8 +148,8 @@ public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode) { * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Activity activity, @NonNull TransparencyMode transparencyMode) { - this(activity, null, RenderMode.surface, transparencyMode); + public FlutterView(@NonNull Context context, @NonNull TransparencyMode transparencyMode) { + this(context, null, RenderMode.surface, transparencyMode); } /** @@ -159,8 +159,8 @@ public FlutterView(@NonNull Activity activity, @NonNull TransparencyMode transpa * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} * to be compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode, @NonNull TransparencyMode transparencyMode) { - this(activity, null, renderMode, transparencyMode); + public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode, @NonNull TransparencyMode transparencyMode) { + this(context, null, renderMode, transparencyMode); } /** @@ -170,12 +170,12 @@ public FlutterView(@NonNull Activity activity, @NonNull RenderMode renderMode, @ * to be compatible with {@link PlatformViewsController}. */ // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr - public FlutterView(@NonNull Activity activity, @Nullable AttributeSet attrs) { - this(activity, attrs, null, null); + public FlutterView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, null, null); } - private FlutterView(@NonNull Activity activity, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode, @Nullable TransparencyMode transparencyMode) { - super(activity, attrs); + private FlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode, @Nullable TransparencyMode transparencyMode) { + super(context, attrs); this.renderMode = renderMode == null ? RenderMode.surface : renderMode; this.transparencyMode = transparencyMode != null ? transparencyMode : TransparencyMode.opaque; From e878c19a3fe5b0c04595ead88f86afa4b245774c Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Fri, 28 Jun 2019 14:50:04 -0700 Subject: [PATCH 4/6] PR Updates. --- .../android/io/flutter/embedding/android/FlutterActivity.java | 2 ++ .../android/io/flutter/embedding/engine/FlutterJNI.java | 1 + 2 files changed, 3 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 3e04584805ab3..f9eabc5015cd4 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -382,6 +382,8 @@ public FlutterEngine provideFlutterEngine(@NonNull Context context) { /** * Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register * plugins. + *

    + * This method is called after {@link #provideFlutterEngine(Context)}. */ @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 9dca768d8fdb9..c478cea46ab0a 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -419,6 +419,7 @@ private native void nativeDispatchPointerDataPacket(long nativePlatformViewId, * See {@link AccessibilityBridge} for an example of an {@link AccessibilityDelegate} and the * surrounding responsibilities. */ + // TODO(mattcarroll): move AccessibilityDelegate definition into FlutterJNI. FlutterJNI should be the basis of dependencies, not the other way round. @UiThread public void setAccessibilityDelegate(@Nullable AccessibilityDelegate accessibilityDelegate) { ensureRunningOnMainThread(); From 7b3009522373481c2f6e0f0c8c8736b091d99d18 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Mon, 1 Jul 2019 02:03:55 -0700 Subject: [PATCH 5/6] PR Updates. --- .../embedding/android/FlutterFragment.java | 16 +++-------- .../embedding/android/FlutterView.java | 15 ++++------ .../embedding/engine/FlutterEngine.java | 17 +++++++++++ .../engine/FlutterEnginePluginRegistry.java | 28 ++++++------------- .../activity/ActivityControlSurface.java | 12 +------- .../activity/ActivityPluginBinding.java | 7 ----- .../engine/plugins/shim/ShimRegistrar.java | 2 +- 7 files changed, 37 insertions(+), 60 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index cecbfb211fa71..4893002a5f280 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -29,7 +29,6 @@ import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; import io.flutter.plugin.platform.PlatformPlugin; -import io.flutter.plugin.platform.PlatformViewsController; import io.flutter.view.FlutterMain; /** @@ -299,8 +298,6 @@ public T build() { private FlutterView flutterView; @Nullable private PlatformPlugin platformPlugin; - @NonNull - private final PlatformViewsController platformViewsController = new PlatformViewsController(); private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener() { @Override @@ -366,8 +363,7 @@ public void onAttach(@NonNull Context context) { Log.d(TAG, "Attaching FlutterEngine to the Activity that owns this Fragment."); flutterEngine.getActivityControlSurface().attachToActivity( getActivity(), - getLifecycle(), - platformViewsController + getLifecycle() ); } @@ -450,7 +446,7 @@ protected FlutterEngine createFlutterEngine(@NonNull Context context) { *

    * This method is called after the given {@link FlutterEngine} has been attached to the * owning {@code FragmentActivity}. See - * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle, PlatformViewsController)}. + * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle)}. *

    * It is possible that the owning {@code FragmentActivity} opted not to connect itself as * an {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that @@ -580,11 +576,7 @@ public void onStart() { @Override public void run() { Log.v(TAG, "Attaching FlutterEngine to FlutterView."); - flutterView.attachToFlutterEngine(flutterEngine, platformViewsController); - - // TODO(mattcarroll): the following call should exist here, but the plugin system needs to be revamped. - // The existing attach() method does not know how to handle this kind of FlutterView. - //flutterEngine.getPlugins().attach(this, getActivity()); + flutterView.attachToFlutterEngine(flutterEngine); doInitialFlutterViewRun(); } @@ -852,7 +844,7 @@ public interface FlutterEngineConfigurator { *

    * This method is called after the given {@link FlutterEngine} has been attached to the * owning {@code FragmentActivity}. See - * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle, PlatformViewsController)}. + * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, Lifecycle)}. *

    * It is possible that the owning {@code FragmentActivity} opted not to connect itself as * an {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 38608a25d749a..b8bc2a44cdab4 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -5,7 +5,6 @@ package io.flutter.embedding.android; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -74,8 +73,6 @@ public class FlutterView extends FrameLayout { @Nullable private FlutterRenderer.RenderSurface renderSurface; private boolean didRenderFirstFrame; - @Nullable - private PlatformViewsController platformViewsController; // Connections to a Flutter execution context. @Nullable @@ -529,8 +526,7 @@ private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExp * {@link FlutterEngine}. */ public void attachToFlutterEngine( - @NonNull FlutterEngine flutterEngine, - @NonNull PlatformViewsController platformViewsController + @NonNull FlutterEngine flutterEngine ) { Log.d(TAG, "Attaching to a FlutterEngine: " + flutterEngine); if (isAttachedToFlutterEngine()) { @@ -547,7 +543,6 @@ public void attachToFlutterEngine( } this.flutterEngine = flutterEngine; - this.platformViewsController = platformViewsController; // Instruct our FlutterRenderer that we are now its designated RenderSurface. didRenderFirstFrame = false; @@ -558,7 +553,7 @@ public void attachToFlutterEngine( textInputPlugin = new TextInputPlugin( this, this.flutterEngine.getDartExecutor(), - platformViewsController + this.flutterEngine.getPlatformViewsController() ); androidKeyProcessor = new AndroidKeyProcessor( this.flutterEngine.getKeyEventChannel(), @@ -570,7 +565,7 @@ public void attachToFlutterEngine( flutterEngine.getAccessibilityChannel(), (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE), getContext().getContentResolver(), - platformViewsController + this.flutterEngine.getPlatformViewsController() ); accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener); resetWillNotDraw( @@ -580,7 +575,7 @@ public void attachToFlutterEngine( // Connect AccessibilityBridge to the PlatformViewsController within the FlutterEngine. // This allows platform Views to hook into Flutter's overall accessibility system. - platformViewsController.attachAccessibilityBridge(accessibilityBridge); + this.flutterEngine.getPlatformViewsController().attachAccessibilityBridge(accessibilityBridge); // Inform the Android framework that it should retrieve a new InputConnection // now that an engine is attached. @@ -621,7 +616,7 @@ public void detachFromFlutterEngine() { } // Disconnect the FlutterEngine's PlatformViewsController from the AccessibilityBridge. - platformViewsController.detachAccessibiltyBridge(); + flutterEngine.getPlatformViewsController().detachAccessibiltyBridge(); // Disconnect and clean up the AccessibilityBridge. accessibilityBridge.release(); diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 08ed9f75004f7..3e2fb2b1c2644 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -29,6 +29,7 @@ import io.flutter.embedding.engine.systemchannels.SettingsChannel; import io.flutter.embedding.engine.systemchannels.SystemChannel; import io.flutter.embedding.engine.systemchannels.TextInputChannel; +import io.flutter.plugin.platform.PlatformViewsController; /** * A single Flutter execution environment. @@ -88,6 +89,11 @@ public class FlutterEngine implements LifecycleOwner { @NonNull private final TextInputChannel textInputChannel; + // Platform Views. + @NonNull + private final PlatformViewsController platformViewsController; + + // Engine Lifecycle. @NonNull private final Set engineLifecycleListeners = new HashSet<>(); @NonNull @@ -138,6 +144,8 @@ public FlutterEngine(@NonNull Context context) { systemChannel = new SystemChannel(dartExecutor); textInputChannel = new TextInputChannel(dartExecutor); + platformViewsController = new PlatformViewsController(); + androidLifecycle = new FlutterEngineAndroidLifecycle(this); this.pluginRegistry = new FlutterEnginePluginRegistry( context.getApplicationContext(), @@ -300,6 +308,15 @@ public PluginRegistry getPlugins() { return pluginRegistry; } + /** + * {@code PlatformViewsController}, which controls all platform views running within + * this {@code FlutterEngine}. + */ + @NonNull + public PlatformViewsController getPlatformViewsController() { + return platformViewsController; + } + @NonNull public ActivityControlSurface getActivityControlSurface() { return pluginRegistry; diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java index 82b89cbf52b3a..173aa915e663c 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java @@ -280,8 +280,7 @@ private boolean isAttachedToActivity() { @Override public void attachToActivity( @NonNull Activity activity, - @NonNull Lifecycle lifecycle, - @NonNull PlatformViewsController platformViewsController + @NonNull Lifecycle lifecycle ) { Log.v(TAG, "Attaching to an Activity: " + activity + "." + (isWaitingForActivityReattachment ? " This is after a config change." : "")); @@ -289,16 +288,13 @@ public void attachToActivity( detachFromAndroidComponent(); this.activity = activity; - this.activityPluginBinding = new FlutterEngineActivityPluginBinding( - activity, - platformViewsController - ); + this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); this.flutterEngineAndroidLifecycle.setBackingLifecycle(lifecycle); // Activate the PlatformViewsController. This must happen before any plugins attempt - // to use it, otherwise an error strack trace will appear that says there is no + // to use it, otherwise an error stack trace will appear that says there is no // flutter/platform_views channel. - platformViewsController.attach( + pluginBinding.getFlutterEngine().getPlatformViewsController().attach( activity, pluginBinding.getFlutterEngine().getRenderer(), pluginBinding.getFlutterEngine().getDartExecutor() @@ -326,7 +322,7 @@ public void detachFromActivityForConfigChanges() { } // Deactivate PlatformViewsController. - activityPluginBinding.platformViewsController.detach(); + pluginBinding.getFlutterEngine().getPlatformViewsController().detach(); flutterEngineAndroidLifecycle.setBackingLifecycle(null); activity = null; @@ -344,6 +340,9 @@ public void detachFromActivity() { activityAware.onDetachedFromActivity(); } + // Deactivate PlatformViewsController. + pluginBinding.getFlutterEngine().getPlatformViewsController().detach(); + flutterEngineAndroidLifecycle.setBackingLifecycle(null); activity = null; activityPluginBinding = null; @@ -524,8 +523,6 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi @NonNull private final Activity activity; @NonNull - private final PlatformViewsController platformViewsController; - @NonNull private final Set onRequestPermissionsResultListeners = new HashSet<>(); @NonNull private final Set onActivityResultListeners = new HashSet<>(); @@ -534,9 +531,8 @@ private static class FlutterEngineActivityPluginBinding implements ActivityPlugi @NonNull private final Set onUserLeaveHintListeners = new HashSet<>(); - public FlutterEngineActivityPluginBinding(@NonNull Activity activity, @NonNull PlatformViewsController platformViewsController) { + public FlutterEngineActivityPluginBinding(@NonNull Activity activity) { this.activity = activity; - this.platformViewsController = platformViewsController; } @Override @@ -545,12 +541,6 @@ public Activity getActivity() { return activity; } - @NonNull - @Override - public PlatformViewsController getPlatformViewsController() { - return platformViewsController; - } - @Override public void addRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) { onRequestPermissionsResultListeners.add(listener); diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java index 19ec3d1354271..0f51c78d9595f 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java @@ -48,11 +48,8 @@ public interface ActivityControlSurface { * executing Dart code, the {@link Activity} should invoke this method. At that point the * {@link FlutterEngine} is considered "attached" to the {@link Activity} and all * {@link ActivityAware} plugins are given access to the {@link Activity}. - *

    - * Flutter expects that platform views are supported whenever a Flutter UI is available, - * therefore, a {@link PlatformViewsController} is required when attaching to an {@code Activity}. */ - void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle, @NonNull PlatformViewsController platformViewsController); + void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle); /** * Call this method from the {@link Activity} that is attached to this {@code ActivityControlSurfaces}'s @@ -61,11 +58,7 @@ public interface ActivityControlSurface { *

    * This method gives each {@link ActivityAware} plugin an opportunity to clean up its references * before the {@link Activity is destroyed}. - *

    - * This method does NOT detach the {@link PlatformViewsController} that was provided in the - * {@link #attachToActivity(Activity, Lifecycle, PlatformViewsController)} method. */ - // TODO(mattcarroll): once platform views are fully updated for lifecycle purposes, reconsider detaching PlatformViewsController here. void detachFromActivityForConfigChanges(); /** @@ -75,9 +68,6 @@ public interface ActivityControlSurface { *

    * This method gives each {@link ActivityAware} plugin an opportunity to clean up its references * before the {@link Activity is destroyed}. - *

    - * Invocation of this method also detaches the {@link PlatformViewsController} that was provided - * in the {@link #attachToActivity(Activity, Lifecycle, PlatformViewsController)} method. */ void detachFromActivity(); diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java index c9b0df3e2338c..2958d79a81322 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java @@ -23,13 +23,6 @@ public interface ActivityPluginBinding { @NonNull Activity getActivity(); - /** - * Returns the {@link PlatformViewsController} that is currently connected to the Flutter UI - * being displayed in the given {@code Activity}. - */ - @NonNull - PlatformViewsController getPlatformViewsController(); - /** * Adds a listener that is invoked whenever the associated {@link Activity}'s * {@code onRequestPermissionsResult(...)} method is invoked. diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java index 813353275b3af..39e5ea5764353 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java @@ -74,7 +74,7 @@ public TextureRegistry textures() { @Override public PlatformViewRegistry platformViewRegistry() { - return activityPluginBinding != null ? activityPluginBinding.getPlatformViewsController().getRegistry() : null; + return pluginBinding != null ? pluginBinding.getFlutterEngine().getPlatformViewsController().getRegistry() : null; } @Override From 6a1f257f970c490f207716f1e376b73527ffa2a5 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Mon, 1 Jul 2019 15:55:09 -0700 Subject: [PATCH 6/6] Implemented checkInputConnectionProxy in FlutterView. --- .../flutter/embedding/android/FlutterView.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index b8bc2a44cdab4..49b933c03ad44 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -18,6 +18,7 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import android.view.WindowInsets; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; @@ -381,6 +382,21 @@ public InputConnection onCreateInputConnection(@NonNull EditorInfo outAttrs) { return textInputPlugin.createInputConnection(this, outAttrs); } + /** + * Allows a {@code View} that is not currently the input connection target to invoke commands on + * the {@link android.view.inputmethod.InputMethodManager}, which is otherwise disallowed. + *

    + * Returns true to allow non-input-connection-targets to invoke methods on + * {@code InputMethodManager}, or false to exclusively allow the input connection target to invoke + * such methods. + */ + @Override + public boolean checkInputConnectionProxy(View view) { + return flutterEngine != null + ? flutterEngine.getPlatformViewsController().checkInputConnectionProxy(view) + : super.checkInputConnectionProxy(view); + } + /** * Invoked when key is released. *