Skip to content

Commit

Permalink
Android plugin registry (flutter#3641)
Browse files Browse the repository at this point in the history
  • Loading branch information
mravn-google authored May 8, 2017
1 parent 33ee0a5 commit b273d1a
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 3 deletions.
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ java_library("flutter_shell_java") {
"io/flutter/plugin/common/MethodCall.java",
"io/flutter/plugin/common/MethodChannel.java",
"io/flutter/plugin/common/MethodCodec.java",
"io/flutter/plugin/common/PluginRegistry.java",
"io/flutter/plugin/common/StandardMessageCodec.java",
"io/flutter/plugin/common/StandardMethodCodec.java",
"io/flutter/plugin/common/StringCodec.java",
Expand Down
122 changes: 119 additions & 3 deletions shell/platform/android/io/flutter/app/FlutterActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView;
import java.util.ArrayList;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Base class for activities that use Flutter.
*/
public class FlutterActivity extends Activity {
public class FlutterActivity extends Activity implements PluginRegistry {
private final Map<String, Object> pluginMap = new LinkedHashMap<>(0);
private final List<RequestPermissionResultListener> requestPermissionResultListeners = new ArrayList<>(0);
private final List<ActivityResultListener> activityResultListeners = new ArrayList<>(0);
private final List<NewIntentListener> newIntentListeners = new ArrayList<>(0);
private final List<UserLeaveHintListener> userLeaveHintListeners = new ArrayList<>(0);
private FlutterView flutterView;

private String[] getArgsFromIntent(Intent intent) {
Expand Down Expand Up @@ -68,6 +79,26 @@ protected void onCreate(Bundle savedInstanceState) {
onFlutterReady();
}

@Override
public boolean hasPlugin(String key) {
return pluginMap.containsKey(key);
}

@Override
@SuppressWarnings("unchecked")
public <T> T valuePublishedByPlugin(String pluginKey) {
return (T) pluginMap.get(pluginKey);
}

@Override
public Registrar registrarFor(String pluginKey) {
if (pluginMap.containsKey(pluginKey)) {
throw new IllegalStateException("Plugin key " + pluginKey + " is already in use");
}
pluginMap.put(pluginKey, null);
return new FlutterRegistrar(pluginKey);
}

/**
* @see android.app.Activity#onDestroy()
*/
Expand Down Expand Up @@ -118,8 +149,39 @@ protected void onFlutterReady() {
}
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
for (RequestPermissionResultListener listener : requestPermissionResultListeners) {
if (listener.onRequestPermissionResult(requestCode, permissions, grantResults)) {
return;
}
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
for (ActivityResultListener listener : activityResultListeners) {
if (listener.onActivityResult(requestCode, resultCode, data)) {
return;
}
}
}

@Override
protected void onNewIntent(Intent intent) {
loadIntent(intent);
if (!loadIntent(intent)) {
for (NewIntentListener listener : newIntentListeners) {
if (listener.onNewIntent(intent)) {
return;
}
}
}
}

@Override
public void onUserLeaveHint() {
for (UserLeaveHintListener listener : userLeaveHintListeners) {
listener.onUserLeaveHint();
}
}

public boolean loadIntent(Intent intent) {
Expand Down Expand Up @@ -159,4 +221,58 @@ public void onTrimMemory(int level) {
if (level == TRIM_MEMORY_RUNNING_LOW)
flutterView.onMemoryPressure();
}

private class FlutterRegistrar implements Registrar {
private final String pluginKey;

FlutterRegistrar(String pluginKey) {
this.pluginKey = pluginKey;
}

public Activity activity() {
return FlutterActivity.this;
}

public BinaryMessenger messenger() {
return getFlutterView();
}

/**
* Publishes a value associated with the plugin being registered.
*
* <p>The published value is available to interested clients via
* {@link PluginRegistry#valuePublishedByPlugin(String)}.</p>
*
* <p>Publication should be done only when there is an interesting value
* to be shared with other code. This would typically be an instance of
* the plugin's main class itself that must be wired up to receive
* notifications or events from an Android API.
*
* <p>Overwrites any previously published value.</p>
*/
public Registrar publish(Object value) {
pluginMap.put(pluginKey, value);
return this;
}

public Registrar addRequestPermissionResultListener(RequestPermissionResultListener listener) {
requestPermissionResultListeners.add(listener);
return this;
}

public Registrar addActivityResultListener(ActivityResultListener listener) {
activityResultListeners.add(listener);
return this;
}

public Registrar addNewIntentListener(NewIntentListener listener) {
newIntentListeners.add(listener);
return this;
}

public Registrar addUserLeaveHintListener(UserLeaveHintListener listener) {
userLeaveHintListeners.add(listener);
return this;
}
};
}
165 changes: 165 additions & 0 deletions shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugin.common;

import android.app.Activity;
import android.content.Intent;

/**
* Registry used by plugins to set up interaction with Android APIs.
*
* <p>Flutter applications by default include an auto-generated and auto-updated
* plugin registrant class (GeneratedPluginRegistrant) that makes use of a
* {@link PluginRegistry} to register contributions from each plugin mentioned
* in the application's pubspec file. The generated registrant class is, again
* by default, called from the application's main {@link Activity}, which
* defaults to an instance of {@link io.flutter.app.FlutterActivity}, itself a
* {@link PluginRegistry}.</p>
*/
public interface PluginRegistry {
/**
* Returns a {@link Registrar} for receiving the registrations pertaining
* to the specified plugin.
*
* @param pluginKey a unique String identifying the plugin; typically the
* fully qualified name of the plugin's main class.
*/
Registrar registrarFor(String pluginKey);

/**
* Returns whether the specified plugin is known to this registry.
*
* @param pluginKey a unique String identifying the plugin; typically the
* fully qualified name of the plugin's main class.
* @return true if this registry has handed out a registrar for the
* specified plugin.
*/
boolean hasPlugin(String pluginKey);

/**
* Returns the value published by the specified plugin, if any.
*
* <p>Plugins may publish a single value, such as an instance of the
* plugin's main class, for situations where external control or
* interaction is needed. Clients are expected to know the value's
* type.</p>
*
* @param pluginKey a unique String identifying the plugin; typically the
* fully qualified name of the plugin's main class.
* @return the published value, possibly null.
*/
<T> T valuePublishedByPlugin(String pluginKey);

/**
* Receiver of registrations from a single plugin.
*/
interface Registrar {
/**
* Returns the {@link Activity} that forms the plugin's operating context.
*/
Activity activity();

/**
* Returns a {@link BinaryMessenger} which the plugin can use for
* creating channels for communicating with the Dart side.
*/
BinaryMessenger messenger();

/**
* Publishes a value associated with the plugin being registered.
*
* <p>The published value is available to interested clients via
* {@link PluginRegistry#valuePublishedByPlugin(String)}.</p>
*
* <p>Publication should be done only when client code needs to interact
* with the plugin in a way that cannot be accomplished by the plugin
* registering callbacks with client APIs.</p>
*
* <p>Overwrites any previously published value.</p>
*
* @param value the value, possibly null.
* @return this {@link Registrar}.
*/
Registrar publish(Object value);

/**
* Adds a callback allowing the plugin to take part in handling incoming
* calls to {@Activity#onRequestPermissionsResult(int, String[], int[])}
* or {android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
*
* @param listener a {@link RequestPermissionResultListener} callback.
* @return this {@link Registrar}.
*/
Registrar addRequestPermissionResultListener(RequestPermissionResultListener listener);

/**
* Adds a callback allowing the plugin to take part in handling incoming
* calls to {@Activity#onActivityResult(int, int, Intent)}.
*
* @param listener an {@link ActivityResultListener} callback.
* @return this {@link Registrar}.
*/
Registrar addActivityResultListener(ActivityResultListener listener);

/**
* Adds a callback allowing the plugin to take part in handling incoming
* calls to {@Activity#onNewIntent(Intent)}.
*
* @param listener a {@link NewIntentListener} callback.
* @return this {@link Registrar}.
*/
Registrar addNewIntentListener(NewIntentListener listener);

/**
* Adds a callback allowing the plugin to take part in handling incoming
* calls to {@Activity#onUserLeaveHint()}.
*
* @param listener a {@link UserLeaveHintListener} callback.
* @return this {@link Registrar}.
*/
Registrar addUserLeaveHintListener(UserLeaveHintListener listener);
}

/**
* Delegate interface for handling results of permission requests on
* behalf of the main {@link Activity}.
*/
interface RequestPermissionResultListener {
/**
* @return true if the result has been handled.
*/
boolean onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults);
}

/**
* Delegate interface for handling activity results on behalf of the main
* {@link Activity}.
*/
interface ActivityResultListener {
/**
* @return true if the result has been handled.
*/
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}

/**
* Delegate interface for handling new intents on behalf of the main
* {@link Activity}.
*/
interface NewIntentListener {
/**
* @return true if the new intent has been handled.
*/
boolean onNewIntent(Intent intent);
}

/**
* Delegate interface for handling user leave hints on behalf of the main
* {@link Activity}.
*/
interface UserLeaveHintListener {
void onUserLeaveHint();
}
}
1 change: 1 addition & 0 deletions travis/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MessageCo
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCall.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java
Expand Down

0 comments on commit b273d1a

Please sign in to comment.