diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 155a0f8612174..0d2e903736f2e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -543,6 +543,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java @@ -553,6 +554,15 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityAware.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverAware.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverControlSurface.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverPluginBinding.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderAware.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderControlSurface.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderPluginBinding.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index a0bc69e7df9f5..d6dfd85120359 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -128,6 +128,7 @@ action("flutter_shell_java") { "io/flutter/embedding/android/FlutterTextureView.java", "io/flutter/embedding/android/FlutterView.java", "io/flutter/embedding/engine/FlutterEngine.java", + "io/flutter/embedding/engine/FlutterEnginePluginRegistry.java", "io/flutter/embedding/engine/FlutterJNI.java", "io/flutter/embedding/engine/FlutterShellArgs.java", "io/flutter/embedding/engine/dart/DartExecutor.java", @@ -138,6 +139,15 @@ action("flutter_shell_java") { "io/flutter/embedding/engine/plugins/activity/ActivityAware.java", "io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java", "io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java", + "io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverAware.java", + "io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverControlSurface.java", + "io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverPluginBinding.java", + "io/flutter/embedding/engine/plugins/contentprovider/ContentProviderAware.java", + "io/flutter/embedding/engine/plugins/contentprovider/ContentProviderControlSurface.java", + "io/flutter/embedding/engine/plugins/contentprovider/ContentProviderPluginBinding.java", + "io/flutter/embedding/engine/plugins/service/ServiceAware.java", + "io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java", + "io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java", "io/flutter/embedding/engine/renderer/FlutterRenderer.java", "io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java", "io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 6bc704f605dbe..1a7612dec8593 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -4,25 +4,15 @@ package io.flutter.embedding.engine; -import android.app.Activity; -import android.arch.lifecycle.Lifecycle; import android.content.Context; -import android.content.Intent; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; import io.flutter.embedding.engine.dart.DartExecutor; -import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.PluginRegistry; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverControlSurface; +import io.flutter.embedding.engine.plugins.contentprovider.ContentProviderControlSurface; +import io.flutter.embedding.engine.plugins.service.ServiceControlSurface; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; import io.flutter.embedding.engine.systemchannels.KeyEventChannel; @@ -283,339 +273,19 @@ public ActivityControlSurface getActivityControlSurface() { return pluginRegistry; } - private static class FlutterEnginePluginRegistry implements PluginRegistry, ActivityControlSurface { - private final Map, FlutterPlugin> plugins = new HashMap<>(); - private final FlutterPlugin.FlutterPluginBinding pluginBinding; - - private final Map, ActivityAware> activityAwarePlugins = new HashMap<>(); - private Activity activity; - private FlutterEngineActivityPluginBinding activityPluginBinding; - - FlutterEnginePluginRegistry( - @NonNull Context appContext, - @NonNull FlutterEngine flutterEngine, - @NonNull Lifecycle lifecycle - ) { - pluginBinding = new FlutterPlugin.FlutterPluginBinding( - appContext, - flutterEngine, - lifecycle - ); - } - - public void add(@NonNull FlutterPlugin plugin) { - // Add the plugin to our generic set of plugins and notify the plugin - // that is has been attached to an engine. - plugins.put(plugin.getClass(), plugin); - plugin.onAttachedToEngine(pluginBinding); - - // For ActivityAware plugins, add the plugin to our set of ActivityAware - // plugins, and if this engine is currently attached to an Activity, - // notify the ActivityAware plugin that it is now attached to an Activity. - if (plugin instanceof ActivityAware) { - ActivityAware activityAware = (ActivityAware) plugin; - activityAwarePlugins.put(plugin.getClass(), activityAware); - - if (isAttachedToActivity()) { - activityAware.onAttachedToActivity(activityPluginBinding); - } - } - - // TODO(mattcarroll): ServiceAware - // TODO(mattcarroll): BroadcastReceiverAware - // TODO(mattcarroll): ContentProviderAware - } - - public void add(@NonNull Set plugins) { - for (FlutterPlugin plugin : plugins) { - add(plugin); - } - } - - public boolean has(@NonNull Class pluginClass) { - return plugins.containsKey(pluginClass); - } - - public FlutterPlugin get(@NonNull Class pluginClass) { - return plugins.get(pluginClass); - } - - public void remove(@NonNull Class pluginClass) { - FlutterPlugin plugin = plugins.get(pluginClass); - if (plugin != null) { - // For ActivityAware plugins, notify the plugin that it is detached from - // an Activity if an Activity is currently attached to this engine. Then - // remove the plugin from our set of ActivityAware plugins. - if (plugin instanceof ActivityAware) { - if (isAttachedToActivity()) { - ActivityAware activityAware = (ActivityAware) plugin; - activityAware.onDetachedFromActivity(); - } - activityAwarePlugins.remove(pluginClass); - } - - // TODO(mattcarroll): ServiceAware - // TODO(mattcarroll): BroadcastReceiverAware - // TODO(mattcarroll): ContentProviderAware - - // Notify the plugin that is now detached from this engine. Then remove - // it from our set of generic plugins. - plugin.onDetachedFromEngine(pluginBinding); - plugins.remove(pluginClass); - } - } - - public void remove(@NonNull Set> pluginClasses) { - for (Class pluginClass : pluginClasses) { - remove(pluginClass); - } - } - - public void removeAll() { - // We copy the keys to a new set so that we can mutate the set while using - // the keys. - remove(new HashSet<>(plugins.keySet())); - plugins.clear(); - } - - //-------- Start ActivityControlSurface ------- - boolean isAttachedToActivity() { - return activity != null; - } - - @Override - public void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle) { - Log.d(TAG, "Attaching to an Activity."); - // If we were already attached to an Activity, detach from it before attaching to - // the new Activity. - if (isAttachedToActivity()) { - for (ActivityAware activityAware : activityAwarePlugins.values()) { - activityAware.onDetachedFromActivity(); - } - } - - this.activity = activity; - this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); - // TODO(mattcarroll): resolve possibility of different lifecycles between this and engine attachment - - // Notify all ActivityAware plugins that they are now attached to a new Activity. - for (ActivityAware activityAware : activityAwarePlugins.values()) { - activityAware.onAttachedToActivity(activityPluginBinding); - } - } - - @Override - public void detachFromActivityForConfigChanges() { - Log.d(TAG, "Detaching from an Activity for config changes."); - if (isAttachedToActivity()) { - for (ActivityAware activityAware : activityAwarePlugins.values()) { - activityAware.onDetachedFromActivityForConfigChanges(); - } - - activity = null; - activityPluginBinding = null; - } else { - Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); - } - } - - @Override - public void reattachToActivityAfterConfigChange(@NonNull Activity activity) { - Log.d(TAG, "Re-attaching to an Activity after config change."); - if (!isAttachedToActivity()) { - this.activity = activity; - activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); - - for (ActivityAware activityAware : activityAwarePlugins.values()) { - activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding); - } - } else { - Log.e(TAG, "Attempted to reattach plugins to an Activity after config changes, but an Activity was already attached."); - } - } - - @Override - public void detachFromActivity() { - Log.d(TAG, "Detaching from an Activity."); - if (isAttachedToActivity()) { - for (ActivityAware activityAware : activityAwarePlugins.values()) { - activityAware.onDetachedFromActivity(); - } - - activity = null; - activityPluginBinding = null; - } else { - Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); - } - } - - @Override - public boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) { - Log.d(TAG, "Forwarding onRequestPermissionsResult() to plugins."); - if (isAttachedToActivity()) { - return activityPluginBinding.onRequestPermissionsResult(requestCode, permissions, grantResult); - } else { - Log.e(TAG, "Attempted to notify ActivityAware plugins of onRequestPermissionsResult, but no Activity was attached."); - return false; - } - } - - @Override - public boolean onActivityResult(int requestCode, int resultCode, @NonNull Intent data) { - Log.d(TAG, "Forwarding onActivityResult() to plugins."); - if (isAttachedToActivity()) { - return activityPluginBinding.onActivityResult(requestCode, resultCode, data); - } else { - Log.e(TAG, "Attempted to notify ActivityAware plugins of onActivityResult, but no Activity was attached."); - return false; - } - } - - @Override - public void onNewIntent(@NonNull Intent intent) { - Log.d(TAG, "Forwarding onNewIntent() to plugins."); - if (isAttachedToActivity()) { - activityPluginBinding.onNewIntent(intent); - } else { - Log.e(TAG, "Attempted to notify ActivityAware plugins of onNewIntent, but no Activity was attached."); - } - } - - @Override - public void onUserLeaveHint() { - Log.d(TAG, "Forwarding onUserLeaveHint() to plugins."); - if (isAttachedToActivity()) { - activityPluginBinding.onUserLeaveHint(); - } else { - Log.e(TAG, "Attempted to notify ActivityAware plugins of onUserLeaveHint, but no Activity was attached."); - } - } - //------- End ActivityControlSurface ----- + @NonNull + public ServiceControlSurface getServiceControlSurface() { + return pluginRegistry; } - private static class FlutterEngineActivityPluginBinding implements ActivityPluginBinding { - private final Activity activity; - private final Set onRequestPermissionsResultListeners = new HashSet<>(); - private final Set onActivityResultListeners = new HashSet<>(); - private final Set onNewIntentListeners = new HashSet<>(); - private final Set onUserLeaveHintListeners = new HashSet<>(); - - public FlutterEngineActivityPluginBinding(@NonNull Activity activity) { - this.activity = activity; - } - - /** - * Returns the {@link Activity} that is currently attached to the {@link FlutterEngine} that - * owns this {@code ActivityPluginBinding}. - */ - @NonNull - public Activity getActivity() { - return activity; - } - - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onRequestPermissionsResult(...)} method is invoked. - */ - 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)}. - */ - public void removeRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) { - onRequestPermissionsResultListeners.remove(listener); - } - - /** - * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its - * associated {@link Activity} has its {@code onRequestPermissionsResult(...)} method invoked. - */ - boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) { - boolean didConsumeResult = false; - for (io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener : onRequestPermissionsResultListeners) { - didConsumeResult = listener.onRequestPermissionsResult(requestCode, permissions, grantResult) || didConsumeResult; - } - return didConsumeResult; - } - - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onActivityResult(...)} method is invoked. - */ - 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)}. - */ - public void removeActivityResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener) { - onActivityResultListeners.remove(listener); - } - - /** - * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its - * associated {@link Activity} has its {@code onActivityResult(...)} method invoked. - */ - boolean onActivityResult(int requestCode, int resultCode, @NonNull Intent data) { - boolean didConsumeResult = false; - for (io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener : onActivityResultListeners) { - didConsumeResult = listener.onActivityResult(requestCode, resultCode, data) || didConsumeResult; - } - return didConsumeResult; - } - - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onNewIntent(...)} method is invoked. - */ - 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)}. - */ - public void removeOnNewIntentListener(@NonNull io.flutter.plugin.common.PluginRegistry.NewIntentListener listener) { - onNewIntentListeners.remove(listener); - } - - /** - * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its - * associated {@link Activity} has its {@code onNewIntent(...)} method invoked. - */ - void onNewIntent(@Nullable Intent intent) { - for (io.flutter.plugin.common.PluginRegistry.NewIntentListener listener : onNewIntentListeners) { - listener.onNewIntent(intent); - } - } - - /** - * Adds a listener that is invoked whenever the associated {@link Activity}'s - * {@code onUserLeaveHint()} method is invoked. - */ - 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)}. - */ - public void removeOnUserLeaveHintListener(@NonNull io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener) { - onUserLeaveHintListeners.remove(listener); - } + @NonNull + public BroadcastReceiverControlSurface getBroadcastReceiverControlSurface() { + return pluginRegistry; + } - /** - * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its - * associated {@link Activity} has its {@code onUserLeaveHint()} method invoked. - */ - void onUserLeaveHint() { - for (io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener : onUserLeaveHintListeners) { - listener.onUserLeaveHint(); - } - } + @NonNull + public ContentProviderControlSurface getContentProviderControlSurface() { + return pluginRegistry; } /** diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java new file mode 100644 index 0000000000000..d79b9d52b41fb --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java @@ -0,0 +1,642 @@ +// Copyright 2013 The Flutter 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.engine; + +import android.app.Activity; +import android.app.Service; +import android.arch.lifecycle.Lifecycle; +import android.content.BroadcastReceiver; +import android.content.ContentProvider; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.PluginRegistry; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverAware; +import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverControlSurface; +import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverPluginBinding; +import io.flutter.embedding.engine.plugins.contentprovider.ContentProviderAware; +import io.flutter.embedding.engine.plugins.contentprovider.ContentProviderControlSurface; +import io.flutter.embedding.engine.plugins.contentprovider.ContentProviderPluginBinding; +import io.flutter.embedding.engine.plugins.service.ServiceAware; +import io.flutter.embedding.engine.plugins.service.ServiceControlSurface; +import io.flutter.embedding.engine.plugins.service.ServicePluginBinding; + +class FlutterEnginePluginRegistry implements PluginRegistry, + ActivityControlSurface, + ServiceControlSurface, + BroadcastReceiverControlSurface, + ContentProviderControlSurface { + private static final String TAG = "EnginePluginRegistry"; + + // PluginRegistry + private final Map, FlutterPlugin> plugins = new HashMap<>(); + + // Standard FlutterPlugin + private final FlutterPlugin.FlutterPluginBinding pluginBinding; + + // ActivityAware + private final Map, ActivityAware> activityAwarePlugins = new HashMap<>(); + private Activity activity; + private FlutterEngineActivityPluginBinding activityPluginBinding; + + // ServiceAware + private final Map, ServiceAware> serviceAwarePlugins = new HashMap<>(); + private Service service; + private FlutterEngineServicePluginBinding servicePluginBinding; + + // BroadcastReceiver + private final Map, BroadcastReceiverAware> broadcastReceiverAwarePlugins = new HashMap<>(); + private BroadcastReceiver broadcastReceiver; + private FlutterEngineBroadcastReceiverPluginBinding broadcastReceiverPluginBinding; + + // ContentProvider + private final Map, ContentProviderAware> contentProviderAwarePlugins = new HashMap<>(); + private ContentProvider contentProvider; + private FlutterEngineContentProviderPluginBinding contentProviderPluginBinding; + + FlutterEnginePluginRegistry( + @NonNull Context appContext, + @NonNull FlutterEngine flutterEngine, + @NonNull Lifecycle lifecycle + ) { + pluginBinding = new FlutterPlugin.FlutterPluginBinding( + appContext, + flutterEngine, + lifecycle + ); + } + + public void add(@NonNull FlutterPlugin plugin) { + // Add the plugin to our generic set of plugins and notify the plugin + // that is has been attached to an engine. + plugins.put(plugin.getClass(), plugin); + plugin.onAttachedToEngine(pluginBinding); + + // For ActivityAware plugins, add the plugin to our set of ActivityAware + // plugins, and if this engine is currently attached to an Activity, + // notify the ActivityAware plugin that it is now attached to an Activity. + if (plugin instanceof ActivityAware) { + ActivityAware activityAware = (ActivityAware) plugin; + activityAwarePlugins.put(plugin.getClass(), activityAware); + + if (isAttachedToActivity()) { + activityAware.onAttachedToActivity(activityPluginBinding); + } + } + + // For ServiceAware plugins, add the plugin to our set of ServiceAware + // plugins, and if this engine is currently attached to a Service, + // notify the ServiceAware plugin that it is now attached to a Service. + if (plugin instanceof ServiceAware) { + ServiceAware serviceAware = (ServiceAware) plugin; + serviceAwarePlugins.put(plugin.getClass(), serviceAware); + + if (isAttachedToService()) { + serviceAware.onAttachedToService(servicePluginBinding); + } + } + + // For BroadcastReceiverAware plugins, add the plugin to our set of BroadcastReceiverAware + // plugins, and if this engine is currently attached to a BroadcastReceiver, + // notify the BroadcastReceiverAware plugin that it is now attached to a BroadcastReceiver. + if (plugin instanceof BroadcastReceiverAware) { + BroadcastReceiverAware broadcastReceiverAware = (BroadcastReceiverAware) plugin; + broadcastReceiverAwarePlugins.put(plugin.getClass(), broadcastReceiverAware); + + if (isAttachedToBroadcastReceiver()) { + broadcastReceiverAware.onAttachedToBroadcastReceiver(broadcastReceiverPluginBinding); + } + } + + // For ContentProviderAware plugins, add the plugin to our set of ContentProviderAware + // plugins, and if this engine is currently attached to a ContentProvider, + // notify the ContentProviderAware plugin that it is now attached to a ContentProvider. + if (plugin instanceof ContentProviderAware) { + ContentProviderAware contentProviderAware = (ContentProviderAware) plugin; + contentProviderAwarePlugins.put(plugin.getClass(), contentProviderAware); + + if (isAttachedToContentProvider()) { + contentProviderAware.onAttachedToContentProvider(contentProviderPluginBinding); + } + } + } + + public void add(@NonNull Set plugins) { + for (FlutterPlugin plugin : plugins) { + add(plugin); + } + } + + public boolean has(@NonNull Class pluginClass) { + return plugins.containsKey(pluginClass); + } + + public FlutterPlugin get(@NonNull Class pluginClass) { + return plugins.get(pluginClass); + } + + public void remove(@NonNull Class pluginClass) { + FlutterPlugin plugin = plugins.get(pluginClass); + if (plugin != null) { + // For ActivityAware plugins, notify the plugin that it is detached from + // an Activity if an Activity is currently attached to this engine. Then + // remove the plugin from our set of ActivityAware plugins. + if (plugin instanceof ActivityAware) { + if (isAttachedToActivity()) { + ActivityAware activityAware = (ActivityAware) plugin; + activityAware.onDetachedFromActivity(); + } + activityAwarePlugins.remove(pluginClass); + } + + // For ServiceAware plugins, notify the plugin that it is detached from + // a Service if a Service is currently attached to this engine. Then + // remove the plugin from our set of ServiceAware plugins. + if (plugin instanceof ServiceAware) { + if (isAttachedToService()) { + ServiceAware serviceAware = (ServiceAware) plugin; + serviceAware.onDetachedFromService(); + } + serviceAwarePlugins.remove(pluginClass); + } + + // For BroadcastReceiverAware plugins, notify the plugin that it is detached from + // a BroadcastReceiver if a BroadcastReceiver is currently attached to this engine. Then + // remove the plugin from our set of BroadcastReceiverAware plugins. + if (plugin instanceof BroadcastReceiverAware) { + if (isAttachedToBroadcastReceiver()) { + BroadcastReceiverAware broadcastReceiverAware = (BroadcastReceiverAware) plugin; + broadcastReceiverAware.onDetachedFromBroadcastReceiver(); + } + broadcastReceiverAwarePlugins.remove(pluginClass); + } + + // For ContentProviderAware plugins, notify the plugin that it is detached from + // a ContentProvider if a ContentProvider is currently attached to this engine. Then + // remove the plugin from our set of ContentProviderAware plugins. + if (plugin instanceof ContentProviderAware) { + if (isAttachedToContentProvider()) { + ContentProviderAware contentProviderAware = (ContentProviderAware) plugin; + contentProviderAware.onDetachedFromContentProvider(); + } + contentProviderAwarePlugins.remove(pluginClass); + } + + // Notify the plugin that is now detached from this engine. Then remove + // it from our set of generic plugins. + plugin.onDetachedFromEngine(pluginBinding); + plugins.remove(pluginClass); + } + } + + public void remove(@NonNull Set> pluginClasses) { + for (Class pluginClass : pluginClasses) { + remove(pluginClass); + } + } + + public void removeAll() { + // We copy the keys to a new set so that we can mutate the set while using + // the keys. + remove(new HashSet<>(plugins.keySet())); + plugins.clear(); + } + + private void detachFromAndroidComponent() { + if (isAttachedToActivity()) { + detachFromActivity(); + } else if (isAttachedToService()) { + detachFromService(); + } else if (isAttachedToBroadcastReceiver()) { + detachFromBroadcastReceiver(); + } else if (isAttachedToContentProvider()) { + detachFromContentProvider(); + } + } + + //-------- Start ActivityControlSurface ------- + private boolean isAttachedToActivity() { + return activity != null; + } + + @Override + public void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle) { + Log.d(TAG, "Attaching to an Activity."); + // If we were already attached to an Android component, detach from it. + detachFromAndroidComponent(); + + this.activity = activity; + this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); + // TODO(mattcarroll): resolve possibility of different lifecycles between this and engine attachment + + // Notify all ActivityAware plugins that they are now attached to a new Activity. + for (ActivityAware activityAware : activityAwarePlugins.values()) { + activityAware.onAttachedToActivity(activityPluginBinding); + } + } + + @Override + public void detachFromActivityForConfigChanges() { + Log.d(TAG, "Detaching from an Activity for config changes."); + if (isAttachedToActivity()) { + for (ActivityAware activityAware : activityAwarePlugins.values()) { + activityAware.onDetachedFromActivityForConfigChanges(); + } + + activity = null; + activityPluginBinding = null; + } else { + Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); + } + } + + @Override + public void reattachToActivityAfterConfigChange(@NonNull Activity activity) { + Log.d(TAG, "Re-attaching to an Activity after config change."); + if (!isAttachedToActivity()) { + this.activity = activity; + activityPluginBinding = new FlutterEngineActivityPluginBinding(activity); + + for (ActivityAware activityAware : activityAwarePlugins.values()) { + activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding); + } + } else { + Log.e(TAG, "Attempted to reattach plugins to an Activity after config changes, but an Activity was already attached."); + } + } + + @Override + public void detachFromActivity() { + Log.d(TAG, "Detaching from an Activity."); + if (isAttachedToActivity()) { + for (ActivityAware activityAware : activityAwarePlugins.values()) { + activityAware.onDetachedFromActivity(); + } + + activity = null; + activityPluginBinding = null; + } else { + Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); + } + } + + @Override + public boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) { + Log.d(TAG, "Forwarding onRequestPermissionsResult() to plugins."); + if (isAttachedToActivity()) { + return activityPluginBinding.onRequestPermissionsResult(requestCode, permissions, grantResult); + } else { + Log.e(TAG, "Attempted to notify ActivityAware plugins of onRequestPermissionsResult, but no Activity was attached."); + return false; + } + } + + @Override + public boolean onActivityResult(int requestCode, int resultCode, @NonNull Intent data) { + Log.d(TAG, "Forwarding onActivityResult() to plugins."); + if (isAttachedToActivity()) { + return activityPluginBinding.onActivityResult(requestCode, resultCode, data); + } else { + Log.e(TAG, "Attempted to notify ActivityAware plugins of onActivityResult, but no Activity was attached."); + return false; + } + } + + @Override + public void onNewIntent(@NonNull Intent intent) { + Log.d(TAG, "Forwarding onNewIntent() to plugins."); + if (isAttachedToActivity()) { + activityPluginBinding.onNewIntent(intent); + } else { + Log.e(TAG, "Attempted to notify ActivityAware plugins of onNewIntent, but no Activity was attached."); + } + } + + @Override + public void onUserLeaveHint() { + Log.d(TAG, "Forwarding onUserLeaveHint() to plugins."); + if (isAttachedToActivity()) { + activityPluginBinding.onUserLeaveHint(); + } else { + Log.e(TAG, "Attempted to notify ActivityAware plugins of onUserLeaveHint, but no Activity was attached."); + } + } + //------- End ActivityControlSurface ----- + + //----- Start ServiceControlSurface ---- + private boolean isAttachedToService() { + return service != null; + } + + @Override + public void attachToService(@NonNull Service service, @NonNull Lifecycle lifecycle, boolean isForeground) { + // If we were already attached to an Android component, detach from it. + detachFromAndroidComponent(); + + this.service = service; + this.servicePluginBinding = new FlutterEngineServicePluginBinding(service); + // TODO(mattcarroll): resolve possibility of different lifecycles between this and engine attachment + + // Notify all ServiceAware plugins that they are now attached to a new Service. + for (ServiceAware serviceAware : serviceAwarePlugins.values()) { + serviceAware.onAttachedToService(servicePluginBinding); + } + } + + @Override + public void detachFromService() { + if (isAttachedToService()) { + // Notify all ServiceAware plugins that they are no longer attached to a Service. + for (ServiceAware serviceAware : serviceAwarePlugins.values()) { + serviceAware.onDetachedFromService(); + } + } else { + Log.e(TAG, "Attempted to detach plugins from a Service when no Service was attached."); + } + } + + @Override + public void onMoveToForeground() { + if (isAttachedToService()) { + servicePluginBinding.onMoveToForeground(); + } + } + + @Override + public void onMoveToBackground() { + if (isAttachedToService()) { + servicePluginBinding.onMoveToBackground(); + } + } + //----- End ServiceControlSurface --- + + //----- Start BroadcastReceiverControlSurface --- + private boolean isAttachedToBroadcastReceiver() { + return broadcastReceiver != null; + } + + @Override + public void attachToBroadcastReceiver(@NonNull BroadcastReceiver broadcastReceiver, @NonNull Lifecycle lifecycle) { + // If we were already attached to an Android component, detach from it. + detachFromAndroidComponent(); + + this.broadcastReceiver = broadcastReceiver; + this.broadcastReceiverPluginBinding = new FlutterEngineBroadcastReceiverPluginBinding(broadcastReceiver); + // TODO(mattcarroll): resolve possibility of different lifecycles between this and engine attachment + + // Notify all BroadcastReceiverAware plugins that they are now attached to a new BroadcastReceiver. + for (BroadcastReceiverAware broadcastReceiverAware : broadcastReceiverAwarePlugins.values()) { + broadcastReceiverAware.onAttachedToBroadcastReceiver(broadcastReceiverPluginBinding); + } + } + + @Override + public void detachFromBroadcastReceiver() { + if (isAttachedToBroadcastReceiver()) { + // Notify all BroadcastReceiverAware plugins that they are no longer attached to a BroadcastReceiver. + for (BroadcastReceiverAware broadcastReceiverAware : broadcastReceiverAwarePlugins.values()) { + broadcastReceiverAware.onDetachedFromBroadcastReceiver(); + } + } else { + Log.e(TAG, "Attempted to detach plugins from a BroadcastReceiver when no BroadcastReceiver was attached."); + } + } + //----- End BroadcastReceiverControlSurface ---- + + //----- Start ContentProviderControlSurface ---- + private boolean isAttachedToContentProvider() { + return contentProvider != null; + } + + @Override + public void attachToContentProvider(@NonNull ContentProvider contentProvider, @NonNull Lifecycle lifecycle) { + // If we were already attached to an Android component, detach from it. + detachFromAndroidComponent(); + + this.contentProvider = contentProvider; + this.contentProviderPluginBinding = new FlutterEngineContentProviderPluginBinding(contentProvider); + // TODO(mattcarroll): resolve possibility of different lifecycles between this and engine attachment + + // Notify all ContentProviderAware plugins that they are now attached to a new ContentProvider. + for (ContentProviderAware contentProviderAware : contentProviderAwarePlugins.values()) { + contentProviderAware.onAttachedToContentProvider(contentProviderPluginBinding); + } + } + + @Override + public void detachFromContentProvider() { + if (isAttachedToContentProvider()) { + // Notify all ContentProviderAware plugins that they are no longer attached to a ContentProvider. + for (ContentProviderAware contentProviderAware : contentProviderAwarePlugins.values()) { + contentProviderAware.onDetachedFromContentProvider(); + } + } else { + Log.e(TAG, "Attempted to detach plugins from a ContentProvider when no ContentProvider was attached."); + } + } + //----- End ContentProviderControlSurface ----- + + private static class FlutterEngineActivityPluginBinding implements ActivityPluginBinding { + private final Activity activity; + private final Set onRequestPermissionsResultListeners = new HashSet<>(); + private final Set onActivityResultListeners = new HashSet<>(); + private final Set onNewIntentListeners = new HashSet<>(); + private final Set onUserLeaveHintListeners = new HashSet<>(); + + public FlutterEngineActivityPluginBinding(@NonNull Activity activity) { + this.activity = activity; + } + + /** + * Returns the {@link Activity} that is currently attached to the {@link FlutterEngine} that + * owns this {@code ActivityPluginBinding}. + */ + @NonNull + public Activity getActivity() { + return activity; + } + + /** + * Adds a listener that is invoked whenever the associated {@link Activity}'s + * {@code onRequestPermissionsResult(...)} method is invoked. + */ + 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)}. + */ + public void removeRequestPermissionsResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener) { + onRequestPermissionsResultListeners.remove(listener); + } + + /** + * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its + * associated {@link Activity} has its {@code onRequestPermissionsResult(...)} method invoked. + */ + boolean onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) { + boolean didConsumeResult = false; + for (io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener listener : onRequestPermissionsResultListeners) { + didConsumeResult = listener.onRequestPermissionsResult(requestCode, permissions, grantResult) || didConsumeResult; + } + return didConsumeResult; + } + + /** + * Adds a listener that is invoked whenever the associated {@link Activity}'s + * {@code onActivityResult(...)} method is invoked. + */ + 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)}. + */ + public void removeActivityResultListener(@NonNull io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener) { + onActivityResultListeners.remove(listener); + } + + /** + * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its + * associated {@link Activity} has its {@code onActivityResult(...)} method invoked. + */ + boolean onActivityResult(int requestCode, int resultCode, @NonNull Intent data) { + boolean didConsumeResult = false; + for (io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener : onActivityResultListeners) { + didConsumeResult = listener.onActivityResult(requestCode, resultCode, data) || didConsumeResult; + } + return didConsumeResult; + } + + /** + * Adds a listener that is invoked whenever the associated {@link Activity}'s + * {@code onNewIntent(...)} method is invoked. + */ + 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)}. + */ + public void removeOnNewIntentListener(@NonNull io.flutter.plugin.common.PluginRegistry.NewIntentListener listener) { + onNewIntentListeners.remove(listener); + } + + /** + * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its + * associated {@link Activity} has its {@code onNewIntent(...)} method invoked. + */ + void onNewIntent(@Nullable Intent intent) { + for (io.flutter.plugin.common.PluginRegistry.NewIntentListener listener : onNewIntentListeners) { + listener.onNewIntent(intent); + } + } + + /** + * Adds a listener that is invoked whenever the associated {@link Activity}'s + * {@code onUserLeaveHint()} method is invoked. + */ + 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)}. + */ + public void removeOnUserLeaveHintListener(@NonNull io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener) { + onUserLeaveHintListeners.remove(listener); + } + + /** + * Invoked by the {@link FlutterEngine} that owns this {@code ActivityPluginBinding} when its + * associated {@link Activity} has its {@code onUserLeaveHint()} method invoked. + */ + void onUserLeaveHint() { + for (io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener listener : onUserLeaveHintListeners) { + listener.onUserLeaveHint(); + } + } + } + + private static class FlutterEngineServicePluginBinding implements ServicePluginBinding { + private final Service service; + private final Set onModeChangeListeners = new HashSet<>(); + + FlutterEngineServicePluginBinding(@NonNull Service service) { + this.service = service; + } + + @NonNull + @Override + public Service getService() { + return service; + } + + @Override + public void addOnModeChangeListener(@NonNull ServiceAware.OnModeChangeListener listener) { + onModeChangeListeners.add(listener); + } + + @Override + public void removeOnModeChangeListener(@NonNull ServiceAware.OnModeChangeListener listener) { + onModeChangeListeners.remove(listener); + } + + void onMoveToForeground() { + for (ServiceAware.OnModeChangeListener listener : onModeChangeListeners) { + listener.onMoveToForeground(); + } + } + + void onMoveToBackground() { + for (ServiceAware.OnModeChangeListener listener : onModeChangeListeners) { + listener.onMoveToBackground(); + } + } + } + + private static class FlutterEngineBroadcastReceiverPluginBinding implements BroadcastReceiverPluginBinding { + private final BroadcastReceiver broadcastReceiver; + + FlutterEngineBroadcastReceiverPluginBinding(@NonNull BroadcastReceiver broadcastReceiver) { + this.broadcastReceiver = broadcastReceiver; + } + + @NonNull + @Override + public BroadcastReceiver getBroadcastReceiver() { + return broadcastReceiver; + } + } + + private static class FlutterEngineContentProviderPluginBinding implements ContentProviderPluginBinding { + private final ContentProvider contentProvider; + + FlutterEngineContentProviderPluginBinding(@NonNull ContentProvider contentProvider) { + this.contentProvider = contentProvider; + } + + @NonNull + @Override + public ContentProvider getContentProvider() { + return contentProvider; + } + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverAware.java b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverAware.java new file mode 100644 index 0000000000000..5842fbc91930a --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverAware.java @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter 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.engine.plugins.broadcastreceiver; + +import android.support.annotation.NonNull; + +/** + * A {@link FlutterPlugin} that wants to know when it is running within a {@link BroadcastReceiver}. + */ +public interface BroadcastReceiverAware { + /** + * Callback triggered when a {@code BroadcastReceiverAware} {@link FlutterPlugin} is associated + * with a {@link BroadcastReceiver}. + */ + void onAttachedToBroadcastReceiver(@NonNull BroadcastReceiverPluginBinding binding); + + /** + * Callback triggered when a {@code BroadcastReceiverAware} {@link FlutterPlugin} is detached from + * a {@link BroadcastReceiver}. + */ + void onDetachedFromBroadcastReceiver(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverControlSurface.java new file mode 100644 index 0000000000000..868438823eec8 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverControlSurface.java @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter 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.engine.plugins.broadcastreceiver; + +import android.content.BroadcastReceiver; +import android.arch.lifecycle.Lifecycle; +import android.support.annotation.NonNull; + +/** + * Control surface through which a {@link BroadcastReceiver} attaches to a {@link FlutterEngine}. + *

+ * A {@link BroadcastReceiver} that contains a {@link FlutterEngine} should coordinate itself with the + * {@link FlutterEngine}'s {@code BroadcastReceiverControlSurface}. + */ +public interface BroadcastReceiverControlSurface { + /** + * Call this method from the {@link BroadcastReceiver} that is running the {@link FlutterEngine} + * that is associated with this {@code BroadcastReceiverControlSurface}. + *

+ * Once a {@link BroadcastReceiver} is created, and its associated {@link FlutterEngine} is + * executing Dart code, the {@link BroadcastReceiver} should invoke this method. At that point the + * {@link FlutterEngine} is considered "attached" to the {@link BroadcastReceiver} and all + * {@link BroadcastReceiverAware} plugins are given access to the {@link BroadcastReceiver}. + */ + void attachToBroadcastReceiver(@NonNull BroadcastReceiver broadcastReceiver, @NonNull Lifecycle lifecycle); + + /** + * Call this method from the {@link BroadcastReceiver} that is attached to this + * {@code BroadcastReceiverControlSurfaces}'s {@link FlutterEngine} when the {@link BroadcastReceiver} + * is about to be destroyed. + *

+ * This method gives each {@link BroadcastReceiverAware} plugin an opportunity to clean up its references + * before the {@link BroadcastReceiver is destroyed}. + */ + void detachFromBroadcastReceiver(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverPluginBinding.java new file mode 100644 index 0000000000000..54ad0971a6ae4 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/broadcastreceiver/BroadcastReceiverPluginBinding.java @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter 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.engine.plugins.broadcastreceiver; + +import android.content.BroadcastReceiver; +import android.support.annotation.NonNull; + +/** + * Binding that gives {@link BroadcastReceiverAware} plugins access to an associated + * {@link BroadcastReceiver}. + */ +public interface BroadcastReceiverPluginBinding { + + /** + * Returns the {@link BroadcastReceiver} that is currently attached to the {@link FlutterEngine} that + * owns this {@code BroadcastReceiverAwarePluginBinding}. + */ + @NonNull + BroadcastReceiver getBroadcastReceiver(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderAware.java b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderAware.java new file mode 100644 index 0000000000000..0879beaaa2c85 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderAware.java @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter 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.engine.plugins.contentprovider; + +import android.support.annotation.NonNull; + +/** + * A {@link FlutterPlugin} that wants to know when it is running within a {@link ContentProvider}. + */ +public interface ContentProviderAware { + /** + * Callback triggered when a {@code ContentProviderAware} {@link FlutterPlugin} is associated with + * a {@link ContentProvider}. + */ + void onAttachedToContentProvider(@NonNull ContentProviderPluginBinding binding); + + /** + * Callback triggered when a {@code ContentProviderAware} {@link FlutterPlugin} is detached from + * a {@link ContentProvider}. + */ + void onDetachedFromContentProvider(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderControlSurface.java new file mode 100644 index 0000000000000..9d84bf89ef55e --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderControlSurface.java @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter 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.engine.plugins.contentprovider; + +import android.content.ContentProvider; +import android.arch.lifecycle.Lifecycle; +import android.support.annotation.NonNull; + +/** + * Control surface through which a {@link ContentProvider} attaches to a {@link FlutterEngine}. + *

+ * A {@link ContentProvider} that contains a {@link FlutterEngine} should coordinate itself with the + * {@link FlutterEngine}'s {@code ContentProviderControlSurface}. + */ +public interface ContentProviderControlSurface { + /** + * Call this method from the {@link ContentProvider} that is running the {@link FlutterEngine} + * that is associated with this {@code ContentProviderControlSurface}. + *

+ * Once a {@link ContentProvider} is created, and its associated {@link FlutterEngine} is + * executing Dart code, the {@link ContentProvider} should invoke this method. At that point the + * {@link FlutterEngine} is considered "attached" to the {@link ContentProvider} and all + * {@link ContentProviderAware} plugins are given access to the {@link ContentProvider}. + */ + void attachToContentProvider(@NonNull ContentProvider contentProvider, @NonNull Lifecycle lifecycle); + + /** + * Call this method from the {@link ContentProvider} that is attached to this + * {@code ContentProviderControlSurfaces}'s {@link FlutterEngine} when the {@link ContentProvider} + * is about to be destroyed. + *

+ * This method gives each {@link ContentProviderAware} plugin an opportunity to clean up its references + * before the {@link ContentProvider is destroyed}. + */ + void detachFromContentProvider(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderPluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderPluginBinding.java new file mode 100644 index 0000000000000..ecc83de1a53f4 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/contentprovider/ContentProviderPluginBinding.java @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter 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.engine.plugins.contentprovider; + +import android.content.ContentProvider; +import android.support.annotation.NonNull; + +/** + * Binding that gives {@link ContentProviderAware} plugins access to an associated + * {@link ContentProvider}. + */ +public interface ContentProviderPluginBinding { + + /** + * Returns the {@link ContentProvider} that is currently attached to the {@link FlutterEngine} that + * owns this {@code ContentProviderAwarePluginBinding}. + */ + @NonNull + ContentProvider getContentProvider(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java new file mode 100644 index 0000000000000..be89a747c5320 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceAware.java @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter 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.engine.plugins.service; + +import android.support.annotation.NonNull; + +/** + * A {@link FlutterPlugin} that wants to know when it is running within a {@link Service}. + */ +public interface ServiceAware { + /** + * Callback triggered when a {@code ServiceAware} {@link FlutterPlugin} is associated with a + * {@link Service}. + */ + void onAttachedToService(@NonNull ServicePluginBinding binding); + + /** + * Callback triggered when a {@code ServiceAware} {@link FlutterPlugin} is detached from a + * {@link Service}. + */ + void onDetachedFromService(); + + interface OnModeChangeListener { + /** + * Callback triggered when the associated {@link Service} goes from background execution to + * foreground execution. + */ + void onMoveToForeground(); + + /** + * Callback triggered when the associated {@link Service} goes from foreground execution to + * background execution. + */ + void onMoveToBackground(); + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java new file mode 100644 index 0000000000000..82c513d6718c4 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServiceControlSurface.java @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter 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.engine.plugins.service; + +import android.app.Service; +import android.arch.lifecycle.Lifecycle; +import android.support.annotation.NonNull; + +/** + * Control surface through which a {@link Service} attaches to a {@link FlutterEngine}. + *

+ * A {@link Service} that contains a {@link FlutterEngine} should coordinate itself with the + * {@link FlutterEngine}'s {@code ServiceControlSurface}. + */ +public interface ServiceControlSurface { + /** + * Call this method from the {@link Service} that is running the {@link FlutterEngine} that is + * associated with this {@code ServiceControlSurface}. + *

+ * Once a {@link Service} is created, and its associated {@link FlutterEngine} is + * executing Dart code, the {@link Service} should invoke this method. At that point the + * {@link FlutterEngine} is considered "attached" to the {@link Service} and all + * {@link ServiceAware} plugins are given access to the {@link Service}. + *

+ * {@code isForeground} should be true if the given {@link Service} is running in the foreground, + * false otherwise. + */ + void attachToService(@NonNull Service service, @NonNull Lifecycle lifecycle, boolean isForeground); + + /** + * Call this method from the {@link Service} that is attached to this {@code ServiceControlSurfaces}'s + * {@link FlutterEngine} when the {@link Service} is about to be destroyed. + *

+ * This method gives each {@link ServiceAware} plugin an opportunity to clean up its references + * before the {@link Service is destroyed}. + */ + void detachFromService(); + + /** + * Call this method from the {@link Service} that is attached to this {@code ServiceControlSurface}'s + * {@link FlutterEngine} when the {@link Service} goes from background to foreground. + */ + void onMoveToForeground(); + + /** + * Call this method from the {@link Service} that is attached to this {@code ServiceControlSurface}'s + * {@link FlutterEngine} when the {@link Service} goes from foreground to background. + */ + void onMoveToBackground(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java new file mode 100644 index 0000000000000..8621bc90a1e81 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter 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.engine.plugins.service; + +import android.app.Service; +import android.support.annotation.NonNull; + +/** + * Binding that gives {@link ServiceAware} plugins access to an associated {@link Service}. + */ +public interface ServicePluginBinding { + + /** + * Returns the {@link Service} that is currently attached to the {@link FlutterEngine} that + * owns this {@code ServicePluginBinding}. + */ + @NonNull + Service getService(); + + /** + * Adds the given {@code listener} to be notified when the associated {@link Service} goes + * from background to foreground, or foreground to background. + */ + void addOnModeChangeListener(@NonNull ServiceAware.OnModeChangeListener listener); + + /** + * Removes the given {@code listener}, which was previously added with + * {@link #addOnModeChangeListener(OnModeChangeListener)}. + */ + void removeOnModeChangeListener(@NonNull ServiceAware.OnModeChangeListener listener); +}