diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 90840267553b..8a0bf83a6b24 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -444,7 +444,13 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/D
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.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/KeyEventChannel.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java
diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn
index 1b49602f25d0..7779ecef8359 100644
--- a/shell/platform/android/BUILD.gn
+++ b/shell/platform/android/BUILD.gn
@@ -113,7 +113,13 @@ java_library("flutter_shell_java") {
"io/flutter/embedding/engine/dart/PlatformMessageHandler.java",
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
"io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java",
+ "io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
+ "io/flutter/embedding/engine/systemchannels/LifecycleChannel.java",
+ "io/flutter/embedding/engine/systemchannels/LocalizationChannel.java",
+ "io/flutter/embedding/engine/systemchannels/NavigationChannel.java",
+ "io/flutter/embedding/engine/systemchannels/PlatformChannel.java",
"io/flutter/embedding/engine/systemchannels/SettingsChannel.java",
+ "io/flutter/embedding/engine/systemchannels/SystemChannel.java",
"io/flutter/plugin/common/ActivityLifecycleListener.java",
"io/flutter/plugin/common/BasicMessageChannel.java",
"io/flutter/plugin/common/BinaryCodec.java",
diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
index 4923f1c77c34..0ee86091ab09 100644
--- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
+++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
@@ -199,7 +199,7 @@ public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformM
@SuppressWarnings("unused")
private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
if (platformMessageHandler != null) {
- platformMessageHandler.handlePlatformMessage(channel, message, replyId);
+ platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}
// TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391)
}
diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
index 28d6f5277201..f7cdd855d77d 100644
--- a/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
+++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
@@ -37,29 +37,33 @@
*/
public class DartExecutor implements BinaryMessenger {
private static final String TAG = "DartExecutor";
-
+
+ @NonNull
private final FlutterJNI flutterJNI;
+ @NonNull
private final DartMessenger messenger;
private boolean isApplicationRunning = false;
-
+
public DartExecutor(@NonNull FlutterJNI flutterJNI) {
this.flutterJNI = flutterJNI;
this.messenger = new DartMessenger(flutterJNI);
}
-
+
/**
* Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this
* {@link DartExecutor} attaches to JNI.
*
* When attached to JNI, this {@link DartExecutor} begins handling 2-way communication to/from
* the Dart execution context. This communication is facilitate via 2 APIs:
- * - {@link BinaryMessenger}, which sends messages to Dart
- * - {@link PlatformMessageHandler}, which receives messages from Dart
+ *
+ *
{@link BinaryMessenger}, which sends messages to Dart
+ *
{@link PlatformMessageHandler}, which receives messages from Dart
+ *
*/
public void onAttachedToJNI() {
flutterJNI.setPlatformMessageHandler(messenger);
}
-
+
/**
* Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this
* {@link DartExecutor} detaches from JNI.
@@ -70,7 +74,7 @@ public void onAttachedToJNI() {
public void onDetachedFromJNI() {
flutterJNI.setPlatformMessageHandler(null);
}
-
+
/**
* Is this {@link DartExecutor} currently executing Dart code?
*
@@ -79,7 +83,7 @@ public void onDetachedFromJNI() {
public boolean isExecutingDart() {
return isApplicationRunning;
}
-
+
/**
* Starts executing Dart code based on the given {@code dartEntrypoint}.
*
@@ -87,12 +91,12 @@ public boolean isExecutingDart() {
*
* @param dartEntrypoint specifies which Dart function to run, and where to find it
*/
- public void executeDartEntrypoint(DartEntrypoint dartEntrypoint) {
+ public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
if (isApplicationRunning) {
Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
return;
}
-
+
flutterJNI.runBundleAndSnapshotFromLibrary(
new String[]{
dartEntrypoint.pathToPrimaryBundle,
@@ -102,10 +106,10 @@ public void executeDartEntrypoint(DartEntrypoint dartEntrypoint) {
null,
dartEntrypoint.androidAssetManager
);
-
+
isApplicationRunning = true;
}
-
+
/**
* Starts executing Dart code based on the given {@code dartCallback}.
*
@@ -113,12 +117,12 @@ public void executeDartEntrypoint(DartEntrypoint dartEntrypoint) {
*
* @param dartCallback specifies which Dart callback to run, and where to find it
*/
- public void executeDartCallback(DartCallback dartCallback) {
+ public void executeDartCallback(@NonNull DartCallback dartCallback) {
if (isApplicationRunning) {
Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
return;
}
-
+
flutterJNI.runBundleAndSnapshotFromLibrary(
new String[]{
dartCallback.pathToPrimaryBundle,
@@ -128,12 +132,12 @@ public void executeDartCallback(DartCallback dartCallback) {
dartCallback.callbackHandle.callbackLibraryPath,
dartCallback.androidAssetManager
);
-
+
isApplicationRunning = true;
}
-
+
//------ START BinaryMessenger -----
-
+
/**
* Sends the given {@code message} from Android to Dart over the given {@code channel}.
*
@@ -141,10 +145,10 @@ public void executeDartCallback(DartCallback dartCallback) {
* @param message the message payload, a direct-allocated {@link ByteBuffer} with the message bytes
*/
@Override
- public void send(String channel, ByteBuffer message) {
+ public void send(@NonNull String channel, @Nullable ByteBuffer message) {
messenger.send(channel, message, null);
}
-
+
/**
* Sends the given {@code messages} from Android to Dart over the given {@code channel} and
* then has the provided {@code callback} invoked when the Dart side responds.
@@ -155,10 +159,10 @@ public void send(String channel, ByteBuffer message) {
* @param callback a callback invoked when the Dart application responds to the message
*/
@Override
- public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply callback) {
+ public void send(@NonNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback) {
messenger.send(channel, message, callback);
}
-
+
/**
* Sets the given {@link io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler} as the
* singular handler for all incoming messages received from the Dart side of this Dart execution
@@ -168,11 +172,11 @@ public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply
* @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null.
*/
@Override
- public void setMessageHandler(String channel, BinaryMessenger.BinaryMessageHandler handler) {
+ public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
messenger.setMessageHandler(channel, handler);
}
//------ END BinaryMessenger -----
-
+
/**
* Configuration options that specify which Dart entrypoint function is executed and where
* to find that entrypoint and other assets required for Dart execution.
@@ -181,23 +185,27 @@ public static class DartEntrypoint {
/**
* Standard Android AssetManager, provided from some {@code Context} or {@code Resources}.
*/
+ @NonNull
public final AssetManager androidAssetManager;
-
+
/**
* The first place that Dart will look for a given function or asset.
*/
+ @NonNull
public final String pathToPrimaryBundle;
-
+
/**
* A secondary fallback location that Dart will look for a given function or asset.
*/
+ @Nullable
public final String pathToFallbackBundle;
-
+
/**
* The name of a Dart function to execute.
*/
+ @NonNull
public final String dartEntrypointFunctionName;
-
+
public DartEntrypoint(
@NonNull AssetManager androidAssetManager,
@NonNull String pathToBundle,
@@ -210,7 +218,7 @@ public DartEntrypoint(
dartEntrypointFunctionName
);
}
-
+
public DartEntrypoint(
@NonNull AssetManager androidAssetManager,
@NonNull String pathToPrimaryBundle,
@@ -223,7 +231,7 @@ public DartEntrypoint(
this.dartEntrypointFunctionName = dartEntrypointFunctionName;
}
}
-
+
/**
* Configuration options that specify which Dart callback function is executed and where
* to find that callback and other assets required for Dart execution.
@@ -233,22 +241,22 @@ public static class DartCallback {
* Standard Android AssetManager, provided from some {@code Context} or {@code Resources}.
*/
public final AssetManager androidAssetManager;
-
+
/**
* The first place that Dart will look for a given function or asset.
*/
public final String pathToPrimaryBundle;
-
+
/**
* A secondary fallback location that Dart will look for a given function or asset.
*/
public final String pathToFallbackBundle;
-
+
/**
* A Dart callback that was previously registered with the Dart VM.
*/
public final FlutterCallbackInformation callbackHandle;
-
+
public DartCallback(
@NonNull AssetManager androidAssetManager,
@NonNull String pathToPrimaryBundle,
@@ -261,7 +269,7 @@ public DartCallback(
callbackHandle
);
}
-
+
public DartCallback(
@NonNull AssetManager androidAssetManager,
@NonNull String pathToPrimaryBundle,
diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
index 16a6b10b8a26..e9fe0a7cd8c0 100644
--- a/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
+++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
@@ -5,6 +5,7 @@
package io.flutter.embedding.engine.dart;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -22,41 +23,50 @@
* IF YOU USE IT, WE WILL BREAK YOU.
*
* See {@link BinaryMessenger}, which sends messages from Android to Dart
+ *
* See {@link PlatformMessageHandler}, which handles messages to Android from Dart
*/
class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
private static final String TAG = "DartMessenger";
-
+
+ @NonNull
private final FlutterJNI flutterJNI;
+ @NonNull
private final Map messageHandlers;
- private final Map mPendingReplies = new HashMap<>();
- private int mNextReplyId = 1;
-
+ @NonNull
+ private final Map pendingReplies;
+ private int nextReplyId = 1;
+
DartMessenger(@NonNull FlutterJNI flutterJNI) {
this.flutterJNI = flutterJNI;
this.messageHandlers = new HashMap<>();
+ this.pendingReplies = new HashMap<>();
}
-
+
@Override
- public void setMessageHandler(String channel, BinaryMessenger.BinaryMessageHandler handler) {
+ public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
if (handler == null) {
messageHandlers.remove(channel);
} else {
messageHandlers.put(channel, handler);
}
}
-
+
@Override
- public void send(String channel, ByteBuffer message) {
+ public void send(@NonNull String channel, @NonNull ByteBuffer message) {
send(channel, message, null);
}
-
+
@Override
- public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply callback) {
+ public void send(
+ @NonNull String channel,
+ @Nullable ByteBuffer message,
+ @Nullable BinaryMessenger.BinaryReply callback
+ ) {
int replyId = 0;
if (callback != null) {
- replyId = mNextReplyId++;
- mPendingReplies.put(replyId, callback);
+ replyId = nextReplyId++;
+ pendingReplies.put(replyId, callback);
}
if (message == null) {
flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
@@ -64,40 +74,30 @@ public void send(String channel, ByteBuffer message, BinaryMessenger.BinaryReply
flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
}
}
-
+
@Override
- public void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
+ public void handleMessageFromDart(
+ @NonNull final String channel,
+ @Nullable byte[] message,
+ final int replyId
+ ) {
BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
if (handler != null) {
try {
final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
- handler.onMessage(buffer, new BinaryMessenger.BinaryReply() {
- private final AtomicBoolean done = new AtomicBoolean(false);
-
- @Override
- public void reply(ByteBuffer reply) {
- if (done.getAndSet(true)) {
- throw new IllegalStateException("Reply already submitted");
- }
- if (reply == null) {
- flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
- } else {
- flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
- }
- }
- });
+ handler.onMessage(buffer, new Reply(flutterJNI, replyId));
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message listener", ex);
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
- return;
+ } else {
+ flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
- flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
-
+
@Override
- public void handlePlatformMessageResponse(int replyId, byte[] reply) {
- BinaryMessenger.BinaryReply callback = mPendingReplies.remove(replyId);
+ public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
+ BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId);
if (callback != null) {
try {
callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
@@ -106,4 +106,28 @@ public void handlePlatformMessageResponse(int replyId, byte[] reply) {
}
}
}
+
+ private static class Reply implements BinaryMessenger.BinaryReply {
+ @NonNull
+ private final FlutterJNI flutterJNI;
+ private final int replyId;
+ private final AtomicBoolean done = new AtomicBoolean(false);
+
+ Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
+ this.flutterJNI = flutterJNI;
+ this.replyId = replyId;
+ }
+
+ @Override
+ public void reply(ByteBuffer reply) {
+ if (done.getAndSet(true)) {
+ throw new IllegalStateException("Reply already submitted");
+ }
+ if (reply == null) {
+ flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
+ } else {
+ flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
index 0403348b871b..ea1011c09abc 100644
--- a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
+++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
@@ -9,7 +9,7 @@
* IF YOU USE IT, WE WILL BREAK YOU.
*/
public interface PlatformMessageHandler {
- void handlePlatformMessage(final String channel, byte[] message, final int replyId);
+ void handleMessageFromDart(final String channel, byte[] message, final int replyId);
void handlePlatformMessageResponse(int replyId, byte[] reply);
}
diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
new file mode 100644
index 000000000000..2ecd048386fc
--- /dev/null
+++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
@@ -0,0 +1,54 @@
+// 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.systemchannels;
+
+import android.support.annotation.NonNull;
+import android.view.KeyEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.flutter.embedding.engine.dart.DartExecutor;
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.JSONMessageCodec;
+
+/**
+ * TODO(mattcarroll): fill in javadoc for KeyEventChannel.
+ */
+public class KeyEventChannel {
+
+ @NonNull
+ public final BasicMessageChannel