Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Return keyboard pressed state #41695

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Expand Up @@ -2312,6 +2312,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/rend
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyboardChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/MouseCursorChannel.java + ../../../flutter/LICENSE
Expand Down Expand Up @@ -2345,6 +2346,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/Listen
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/SpellCheckPlugin.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextEditingDelta.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/keyboard/KeyboardPlugin.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/mouse/MouseCursorPlugin.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java + ../../../flutter/LICENSE
Expand Down Expand Up @@ -4912,6 +4914,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/render
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyboardChannel.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/MouseCursorChannel.java
Expand Down Expand Up @@ -4948,6 +4951,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/Listenab
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/SpellCheckPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextEditingDelta.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/keyboard/KeyboardPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/mouse/MouseCursorPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/android/BUILD.gn
Expand Up @@ -247,6 +247,7 @@ android_java_sources = [
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",
"io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyboardChannel.java",
"io/flutter/embedding/engine/systemchannels/LifecycleChannel.java",
"io/flutter/embedding/engine/systemchannels/LocalizationChannel.java",
"io/flutter/embedding/engine/systemchannels/MouseCursorChannel.java",
Expand Down Expand Up @@ -283,6 +284,7 @@ android_java_sources = [
"io/flutter/plugin/editing/SpellCheckPlugin.java",
"io/flutter/plugin/editing/TextEditingDelta.java",
"io/flutter/plugin/editing/TextInputPlugin.java",
"io/flutter/plugin/keyboard/KeyboardPlugin.java",
"io/flutter/plugin/localization/LocalizationPlugin.java",
"io/flutter/plugin/mouse/MouseCursorPlugin.java",
"io/flutter/plugin/platform/AccessibilityEventsDelegate.java",
Expand Down
Expand Up @@ -62,6 +62,7 @@
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.editing.SpellCheckPlugin;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.keyboard.KeyboardPlugin;
import io.flutter.plugin.localization.LocalizationPlugin;
import io.flutter.plugin.mouse.MouseCursorPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
Expand Down Expand Up @@ -130,6 +131,7 @@ public class FlutterView extends FrameLayout
@Nullable private MouseCursorPlugin mouseCursorPlugin;
@Nullable private TextInputPlugin textInputPlugin;
@Nullable private SpellCheckPlugin spellCheckPlugin;
@Nullable private KeyboardPlugin keyboardPlugin;
@Nullable private LocalizationPlugin localizationPlugin;
@Nullable private KeyboardManager keyboardManager;
@Nullable private AndroidTouchProcessor androidTouchProcessor;
Expand Down Expand Up @@ -1175,6 +1177,8 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
localizationPlugin = this.flutterEngine.getLocalizationPlugin();

keyboardManager = new KeyboardManager(this);
keyboardPlugin = new KeyboardPlugin(keyboardManager, this.flutterEngine.getKeyboardChannel());

androidTouchProcessor =
new AndroidTouchProcessor(this.flutterEngine.getRenderer(), /*trackMotionEvents=*/ false);
accessibilityBridge =
Expand Down Expand Up @@ -1271,10 +1275,12 @@ public void detachFromFlutterEngine() {
if (spellCheckPlugin != null) {
spellCheckPlugin.destroy();
}

if (mouseCursorPlugin != null) {
mouseCursorPlugin.destroy();
}
if (keyboardPlugin != null) {
keyboardPlugin.destroy();
}

// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
FlutterRenderer flutterRenderer = flutterEngine.getRenderer();
Expand Down
Expand Up @@ -11,7 +11,9 @@
import io.flutter.embedding.android.KeyboardMap.TogglingGoal;
import io.flutter.plugin.common.BinaryMessenger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* A {@link KeyboardManager.Responder} of {@link KeyboardManager} that handles events by sending
Expand Down Expand Up @@ -405,4 +407,14 @@ public void handleEvent(
onKeyEventHandledCallback.onKeyEventHandled(true);
}
}

/**
* Returns an unmodifiable view of the pressed state.
*
* @return A map whose keys are physical keyboard key IDs and values are the corresponding logical
* keyboard key IDs.
*/
public Map<Long, Long> getPressedState() {
return Collections.unmodifiableMap(pressingRecords);
}
}
Expand Up @@ -13,6 +13,7 @@
import io.flutter.plugin.editing.InputConnectionAdaptor;
import io.flutter.plugin.editing.TextInputPlugin;
import java.util.HashSet;
import java.util.Map;

/**
* Processes keyboard events and cooperate with {@link TextInputPlugin}.
Expand Down Expand Up @@ -252,4 +253,15 @@ private void onUnhandled(@NonNull KeyEvent keyEvent) {
Log.w(TAG, "A redispatched key event was consumed before reaching KeyboardManager");
}
}

/**
* Returns an unmodifiable view of the pressed state.
*
* @return A map whose keys are physical keyboard key IDs and values are the corresponding logical
* keyboard key IDs.
*/
public Map<Long, Long> getPressedState() {
KeyEmbedderResponder embedderResponder = (KeyEmbedderResponder) responders[0];
return embedderResponder.getPressedState();
}
}
Expand Up @@ -26,6 +26,7 @@
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
import io.flutter.embedding.engine.systemchannels.KeyboardChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
Expand Down Expand Up @@ -88,6 +89,7 @@ public class FlutterEngine {
// System channels.
@NonNull private final AccessibilityChannel accessibilityChannel;
@NonNull private final DeferredComponentChannel deferredComponentChannel;
@NonNull private final KeyboardChannel keyboardChannel;
@NonNull private final LifecycleChannel lifecycleChannel;
@NonNull private final LocalizationChannel localizationChannel;
@NonNull private final MouseCursorChannel mouseCursorChannel;
Expand Down Expand Up @@ -323,6 +325,7 @@ public FlutterEngine(

accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
keyboardChannel = new KeyboardChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
mouseCursorChannel = new MouseCursorChannel(dartExecutor);
Expand Down Expand Up @@ -513,6 +516,12 @@ public AccessibilityChannel getAccessibilityChannel() {
return accessibilityChannel;
}

/** System channel that allows querying the keyboard pressed state. */
@NonNull
public KeyboardChannel getKeyboardChannel() {
return keyboardChannel;
}

/** System channel that sends Android lifecycle events to Flutter. */
@NonNull
public LifecycleChannel getLifecycleChannel() {
Expand Down
@@ -0,0 +1,73 @@
// 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.StandardMethodCodec;
import java.util.HashMap;
import java.util.Map;

/**
* Event message channel for keyboard events to/from the Flutter framework.
*
* <p>Receives asynchronous messages from the framework to query the engine known pressed state.
*/
public class KeyboardChannel {
private static final String TAG = "KeyboardChannel";

public final MethodChannel channel;
private KeyboardMethodHandler keyboardMethodHandler;

@NonNull
public final MethodChannel.MethodCallHandler parsingMethodHandler =
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (keyboardMethodHandler == null) {
return;
}
switch (call.method) {
case "getKeyboardState":
Map<Long, Long> pressedState = new HashMap<>();
try {
pressedState = keyboardMethodHandler.getKeyboardState();
} catch (IllegalStateException exception) {
result.error("error", exception.getMessage(), null);
}
result.success(pressedState);
break;
default:
result.notImplemented();
break;
}
}
};

public KeyboardChannel(@NonNull DartExecutor dartExecutor) {
channel = new MethodChannel(dartExecutor, "flutter/keyboard", StandardMethodCodec.INSTANCE);
channel.setMethodCallHandler(parsingMethodHandler);
}

/**
* Sets the {@link KeyboardMethodHandler} which receives all requests to query the keyboard state.
*/
public void setKeyboardMethodHandler(@Nullable KeyboardMethodHandler keyboardMethodHandler) {
this.keyboardMethodHandler = keyboardMethodHandler;
}

public interface KeyboardMethodHandler {
/**
* Returns the keyboard pressed states.
*
* @return A map whose keys are physical keyboard key IDs and values are the corresponding
* logical keyboard key IDs.
*/
Map<Long, Long> getKeyboardState();
}
}
@@ -0,0 +1,57 @@
// 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.plugin.keyboard;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.systemchannels.KeyboardChannel;
import io.flutter.plugin.common.MethodChannel;
import java.util.Map;

/**
* {@link KeyboardPlugin} is the implementation of all functionalities needed for querying keyboard
* pressed state.
*
* <p>The plugin handles requests for querying keyboard pressed states by the {@link
* io.flutter.embedding.engine.systemchannels.KeyboardChannel} via returning the {@link
* io.flutter.embedding.android.KeyEmbedderResponder} pressed keys.
*/
public class KeyboardPlugin implements KeyboardChannel.KeyboardMethodHandler {

private final KeyboardChannel mKeyboardChannel;
private final KeyboardManager mKeyboardManager;

@VisibleForTesting MethodChannel.Result pendingResult;

public KeyboardPlugin(
@NonNull KeyboardManager keyboardManager, @NonNull KeyboardChannel keyboardChannel) {
mKeyboardManager = keyboardManager;
mKeyboardChannel = keyboardChannel;

mKeyboardChannel.setKeyboardMethodHandler(this);
}

/**
* Unregisters this {@code KeyboardPlugin} as the {@code KeyboardChannel.KeyboardMethodHandler},
* for the {@link io.flutter.embedding.engine.systemchannels.KeyboardChannel}.
*
* <p>Do not invoke any methods on a {@code KeyboardPlugin} after invoking this method.
*/
public void destroy() {
mKeyboardChannel.setKeyboardMethodHandler(null);
}

/**
* Returns the keyboard pressed state.
*
* @return A map whose keys are physical keyboard key IDs and values are the corresponding logical
* keyboard key IDs.
*/
@Override
public Map<Long, Long> getKeyboardState() {
return mKeyboardManager.getPressedState();
}
}
7 changes: 7 additions & 0 deletions shell/platform/android/io/flutter/view/FlutterView.java
Expand Up @@ -48,6 +48,7 @@
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.SurfaceTextureWrapper;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.KeyboardChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
Expand All @@ -59,6 +60,7 @@
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.keyboard.KeyboardPlugin;
import io.flutter.plugin.localization.LocalizationPlugin;
import io.flutter.plugin.mouse.MouseCursorPlugin;
import io.flutter.plugin.platform.PlatformPlugin;
Expand Down Expand Up @@ -124,12 +126,14 @@ static final class ViewportMetrics {
private final FlutterRenderer flutterRenderer;
private final NavigationChannel navigationChannel;
private final LifecycleChannel lifecycleChannel;
private final KeyboardChannel keyboardChannel;
private final LocalizationChannel localizationChannel;
private final PlatformChannel platformChannel;
private final SettingsChannel settingsChannel;
private final SystemChannel systemChannel;
private final InputMethodManager mImm;
private final TextInputPlugin mTextInputPlugin;
private final KeyboardPlugin mKeyboardPlugin;
private final LocalizationPlugin mLocalizationPlugin;
private final MouseCursorPlugin mMouseCursorPlugin;
private final KeyboardManager mKeyboardManager;
Expand Down Expand Up @@ -213,6 +217,7 @@ public void surfaceDestroyed(SurfaceHolder holder) {

// Create all platform channels
navigationChannel = new NavigationChannel(dartExecutor);
keyboardChannel = new KeyboardChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
Expand All @@ -233,7 +238,9 @@ public void onPostResume() {
mNativeView.getPluginRegistry().getPlatformViewsController();
mTextInputPlugin =
new TextInputPlugin(this, new TextInputChannel(dartExecutor), platformViewsController);

mKeyboardManager = new KeyboardManager(this);
mKeyboardPlugin = new KeyboardPlugin(mKeyboardManager, keyboardChannel);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mMouseCursorPlugin = new MouseCursorPlugin(this, new MouseCursorChannel(dartExecutor));
Expand Down
Expand Up @@ -39,6 +39,7 @@
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.KeyboardChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
Expand Down Expand Up @@ -1293,6 +1294,7 @@ private FlutterEngine mockFlutterEngine() {
when(engine.getAccessibilityChannel()).thenReturn(mock(AccessibilityChannel.class));
when(engine.getActivityControlSurface()).thenReturn(mock(ActivityControlSurface.class));
when(engine.getDartExecutor()).thenReturn(mock(DartExecutor.class));
when(engine.getKeyboardChannel()).thenReturn(mock(KeyboardChannel.class));
when(engine.getLifecycleChannel()).thenReturn(mock(LifecycleChannel.class));
when(engine.getLocalizationChannel()).thenReturn(mock(LocalizationChannel.class));
when(engine.getLocalizationPlugin()).thenReturn(mock(LocalizationPlugin.class));
Expand Down