diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn
index 6a941dba74acb..93e6b140a7304 100644
--- a/shell/platform/android/BUILD.gn
+++ b/shell/platform/android/BUILD.gn
@@ -430,7 +430,6 @@ action("robolectric_tests") {
sources = [
"test/io/flutter/FlutterTestSuite.java",
"test/io/flutter/SmokeTest.java",
- "test/io/flutter/embedding/android/AndroidKeyProcessorTest.java",
"test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java",
"test/io/flutter/embedding/android/FlutterActivityTest.java",
"test/io/flutter/embedding/android/FlutterAndroidComponentTest.java",
@@ -448,7 +447,6 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
- "test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java",
"test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java",
"test/io/flutter/external/FlutterLaunchTests.java",
"test/io/flutter/plugin/common/StandardMessageCodecTest.java",
diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java
index d0d5c1d68cdab..5d823ef8ab674 100644
--- a/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java
+++ b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java
@@ -4,122 +4,37 @@
package io.flutter.embedding.android;
-import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.plugin.editing.TextInputPlugin;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.Map.Entry;
-/**
- * A class to process key events from Android, passing them to the framework as messages using
- * {@link KeyEventChannel}.
- *
- *
A class that sends Android key events to the framework, and re-dispatches those not handled by
- * the framework.
- *
- *
Flutter uses asynchronous event handling to avoid blocking the UI thread, but Android requires
- * that events are handled synchronously. So, when a key event is received by Flutter, it tells
- * Android synchronously that the key has been handled so that it won't propagate to other
- * components. Flutter then uses "delayed event synthesis", where it sends the event to the
- * framework, and if the framework responds that it has not handled the event, then this class
- * synthesizes a new event to send to Android, without handling it this time.
- */
public class AndroidKeyProcessor {
- private static final String TAG = "AndroidKeyProcessor";
- private static long eventIdSerial = 0;
-
@NonNull private final KeyEventChannel keyEventChannel;
@NonNull private final TextInputPlugin textInputPlugin;
private int combiningCharacter;
- @NonNull private EventResponder eventResponder;
- /**
- * Constructor for AndroidKeyProcessor.
- *
- *
The view is used as the destination to send the synthesized key to. This means that the the
- * next thing in the focus chain will get the event when the framework returns false from
- * onKeyDown/onKeyUp
- *
- *
It is possible that that in the middle of the async round trip, the focus chain could
- * change, and instead of the native widget that was "next" when the event was fired getting the
- * event, it may be the next widget when the event is synthesized that gets it. In practice, this
- * shouldn't be a huge problem, as this is an unlikely occurance to happen without user input, and
- * it may actually be desired behavior, but it is possible.
- *
- * @param view takes the activity to use for re-dispatching of events that were not handled by the
- * framework.
- * @param keyEventChannel the event channel to listen to for new key events.
- * @param textInputPlugin a plugin, which, if set, is given key events before the framework is,
- * and if it has a valid input connection and is accepting text, then it will handle the event
- * and the framework will not receive it.
- */
public AndroidKeyProcessor(
- @NonNull View view,
- @NonNull KeyEventChannel keyEventChannel,
- @NonNull TextInputPlugin textInputPlugin) {
+ @NonNull KeyEventChannel keyEventChannel, @NonNull TextInputPlugin textInputPlugin) {
this.keyEventChannel = keyEventChannel;
this.textInputPlugin = textInputPlugin;
- this.eventResponder = new EventResponder(view);
- this.keyEventChannel.setEventResponseHandler(eventResponder);
}
- /**
- * Called when a key up event is received by the {@link FlutterView}.
- *
- * @param keyEvent the Android key event to respond to.
- * @return true if the key event should not be propagated to other Android components. Delayed
- * synthesis events will return false, so that other components may handle them.
- */
- public boolean onKeyUp(@NonNull KeyEvent keyEvent) {
- if (eventResponder.dispatchingKeyEvent) {
- // Don't handle it if it is from our own delayed event synthesis.
- return false;
- }
-
+ public void onKeyUp(@NonNull KeyEvent keyEvent) {
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
- KeyEventChannel.FlutterKeyEvent flutterEvent =
- new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter, eventIdSerial++);
- keyEventChannel.keyUp(flutterEvent);
- eventResponder.addEvent(flutterEvent.eventId, keyEvent);
- return true;
+ keyEventChannel.keyUp(new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter));
}
- /**
- * Called when a key down event is received by the {@link FlutterView}.
- *
- * @param keyEvent the Android key event to respond to.
- * @return true if the key event should not be propagated to other Android components. Delayed
- * synthesis events will return false, so that other components may handle them.
- */
- public boolean onKeyDown(@NonNull KeyEvent keyEvent) {
- if (eventResponder.dispatchingKeyEvent) {
- // Don't handle it if it is from our own delayed event synthesis.
- return false;
- }
-
- // If the textInputPlugin is still valid and accepting text, then we'll try
- // and send the key event to it, assuming that if the event can be sent,
- // that it has been handled.
+ public void onKeyDown(@NonNull KeyEvent keyEvent) {
if (textInputPlugin.getLastInputConnection() != null
&& textInputPlugin.getInputMethodManager().isAcceptingText()) {
- if (textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent)) {
- return true;
- }
+ textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent);
}
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
- KeyEventChannel.FlutterKeyEvent flutterEvent =
- new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter, eventIdSerial++);
- keyEventChannel.keyDown(flutterEvent);
- eventResponder.addEvent(flutterEvent.eventId, keyEvent);
- return true;
+ keyEventChannel.keyDown(new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter));
}
/**
@@ -155,7 +70,7 @@ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoi
return null;
}
- char complexCharacter = (char) newCharacterCodePoint;
+ Character complexCharacter = (char) newCharacterCodePoint;
boolean isNewCodePointACombiningCharacter =
(newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
if (isNewCodePointACombiningCharacter) {
@@ -167,8 +82,7 @@ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoi
combiningCharacter = plainCodePoint;
}
} else {
- // The new character is a regular character. Apply combiningCharacter to it, if
- // it exists.
+ // The new character is a regular character. Apply combiningCharacter to it, if it exists.
if (combiningCharacter != 0) {
int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint);
if (combinedChar > 0) {
@@ -180,92 +94,4 @@ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoi
return complexCharacter;
}
-
- private static class EventResponder implements KeyEventChannel.EventResponseHandler {
- // The maximum number of pending events that are held before starting to
- // complain.
- private static final long MAX_PENDING_EVENTS = 1000;
- final Deque> pendingEvents = new ArrayDeque>();
- @NonNull private final View view;
- boolean dispatchingKeyEvent = false;
-
- public EventResponder(@NonNull View view) {
- this.view = view;
- }
-
- /**
- * Removes the pending event with the given id from the cache of pending events.
- *
- * @param id the id of the event to be removed.
- */
- private KeyEvent removePendingEvent(long id) {
- if (pendingEvents.getFirst().getKey() != id) {
- throw new AssertionError(
- "Event response received out of order. Should have seen event "
- + pendingEvents.getFirst().getKey()
- + " first. Instead, received "
- + id);
- }
- return pendingEvents.removeFirst().getValue();
- }
-
- /**
- * Called whenever the framework responds that a given key event was handled by the framework.
- *
- * @param id the event id of the event to be marked as being handled by the framework. Must not
- * be null.
- */
- @Override
- public void onKeyEventHandled(long id) {
- removePendingEvent(id);
- }
-
- /**
- * Called whenever the framework responds that a given key event wasn't handled by the
- * framework.
- *
- * @param id the event id of the event to be marked as not being handled by the framework. Must
- * not be null.
- */
- @Override
- public void onKeyEventNotHandled(long id) {
- dispatchKeyEvent(removePendingEvent(id));
- }
-
- /** Adds an Android key event with an id to the event responder to wait for a response. */
- public void addEvent(long id, @NonNull KeyEvent event) {
- if (pendingEvents.size() > 0 && pendingEvents.getFirst().getKey() >= id) {
- throw new AssertionError(
- "New events must have ids greater than the most recent pending event. New id "
- + id
- + " is less than or equal to the last event id of "
- + pendingEvents.getFirst().getKey());
- }
- pendingEvents.addLast(new SimpleImmutableEntry(id, event));
- if (pendingEvents.size() > MAX_PENDING_EVENTS) {
- Log.e(
- TAG,
- "There are "
- + pendingEvents.size()
- + " keyboard events "
- + "that have not yet received a response. Are responses being sent?");
- }
- }
-
- /**
- * Dispatches the event to the activity associated with the context.
- *
- * @param event the event to be dispatched to the activity.
- */
- public void dispatchKeyEvent(KeyEvent event) {
- // Since the framework didn't handle it, dispatch the key again.
- if (view != null) {
- // Turn on dispatchingKeyEvent so that we don't dispatch to ourselves and
- // send it to the framework again.
- dispatchingKeyEvent = true;
- view.dispatchKeyEvent(event);
- dispatchingKeyEvent = false;
- }
- }
- }
}
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java
index fa198d38190f0..8cf69c24cf568 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java
@@ -679,7 +679,8 @@ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
return super.onKeyUp(keyCode, event);
}
- return androidKeyProcessor.onKeyUp(event) || super.onKeyUp(keyCode, event);
+ androidKeyProcessor.onKeyUp(event);
+ return super.onKeyUp(keyCode, event);
}
/**
@@ -699,7 +700,8 @@ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
return super.onKeyDown(keyCode, event);
}
- return androidKeyProcessor.onKeyDown(event) || super.onKeyDown(keyCode, event);
+ androidKeyProcessor.onKeyDown(event);
+ return super.onKeyDown(keyCode, event);
}
/**
@@ -849,7 +851,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
this.flutterEngine.getPlatformViewsController());
localizationPlugin = this.flutterEngine.getLocalizationPlugin();
androidKeyProcessor =
- new AndroidKeyProcessor(this, this.flutterEngine.getKeyEventChannel(), textInputPlugin);
+ new AndroidKeyProcessor(this.flutterEngine.getKeyEventChannel(), textInputPlugin);
androidTouchProcessor =
new AndroidTouchProcessor(this.flutterEngine.getRenderer(), /*trackMotionEvents=*/ false);
accessibilityBridge =
diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
index 87db8cf09f245..90455bcecefc2 100644
--- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
+++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
@@ -73,7 +73,6 @@ public void onMessage(
break;
}
}
- reply.reply(null);
}
};
diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
index 3638c2d364ae7..1b03c94184221 100644
--- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
+++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
@@ -9,104 +9,29 @@
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import io.flutter.Log;
+import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.BasicMessageChannel;
-import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.JSONMessageCodec;
import java.util.HashMap;
import java.util.Map;
-import org.json.JSONException;
-import org.json.JSONObject;
-/**
- * Event message channel for key events to/from the Flutter framework.
- *
- *
Sends key up/down events to the framework, and receives asynchronous messages from the
- * framework about whether or not the key was handled.
- */
+/** TODO(mattcarroll): fill in javadoc for KeyEventChannel. */
public class KeyEventChannel {
- private static final String TAG = "KeyEventChannel";
- /**
- * Sets the event response handler to be used to receive key event response messages from the
- * framework on this channel.
- */
- public void setEventResponseHandler(EventResponseHandler handler) {
- this.eventResponseHandler = handler;
- }
-
- private EventResponseHandler eventResponseHandler;
-
- /** A handler of incoming key handling messages. */
- public interface EventResponseHandler {
-
- /**
- * Called whenever the framework responds that a given key event was handled by the framework.
- *
- * @param id the event id of the event to be marked as being handled by the framework. Must not
- * be null.
- */
- public void onKeyEventHandled(long id);
-
- /**
- * Called whenever the framework responds that a given key event wasn't handled by the
- * framework.
- *
- * @param id the event id of the event to be marked as not being handled by the framework. Must
- * not be null.
- */
- public void onKeyEventNotHandled(long id);
- }
+ @NonNull public final BasicMessageChannel