-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Android] Return the keyboard pressed state (#42758)
## Description This PR updates the Android engine in order to answer to keyboard pressed state queries from the framework (as implemented in flutter/flutter#122885). This is a rework of #41695 which was reverted in #42346. This issue with #41695 was that the framework side did not get an answer when the channel was setup in the engine without registering a handler (on the engine side) to handle framework requests. The issue was reproducible when the engine initialization was managed by the app (see flutter/flutter#122441 (comment) for a repro). This PR fixes this issue by changing `flutter/keyboard` lifecycle: the engine now creates the channel and registers a handler just after the channel creation. In order to avoid regression, this PR also updates the channel implemenation (see `KeyboardChannel`) to return an empty `HashMap` when there is no handler registered. ## Related Issue Android engine implementation for flutter/flutter#87391 (see #42346 for Linux implementation) Fixes flutter/flutter#122441 ## Tests Adds 3 tests.
- Loading branch information
Showing
8 changed files
with
194 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyboardChannel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// 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.plugin.common.BinaryMessenger; | ||
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 { | ||
public final MethodChannel channel; | ||
private KeyboardMethodHandler keyboardMethodHandler; | ||
|
||
@NonNull | ||
public final MethodChannel.MethodCallHandler parsingMethodHandler = | ||
new MethodChannel.MethodCallHandler() { | ||
Map<Long, Long> pressedState = new HashMap<>(); | ||
|
||
@Override | ||
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { | ||
if (keyboardMethodHandler == null) { | ||
// Returns an empty pressed state when the engine did not get a chance to register | ||
// a method handler for this channel. | ||
result.success(pressedState); | ||
} else { | ||
switch (call.method) { | ||
case "getKeyboardState": | ||
try { | ||
pressedState = keyboardMethodHandler.getKeyboardState(); | ||
} catch (IllegalStateException exception) { | ||
result.error("error", exception.getMessage(), null); | ||
} | ||
result.success(pressedState); | ||
break; | ||
default: | ||
result.notImplemented(); | ||
break; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
public KeyboardChannel(@NonNull BinaryMessenger messenger) { | ||
channel = new MethodChannel(messenger, "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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
...platform/android/test/io/flutter/embedding/engine/systemchannels/KeyboardChannelTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package io.flutter.embedding.android; | ||
|
||
import static org.mockito.Mockito.any; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
|
||
import androidx.test.ext.junit.runners.AndroidJUnit4; | ||
import io.flutter.embedding.engine.dart.DartExecutor; | ||
import io.flutter.embedding.engine.systemchannels.KeyboardChannel; | ||
import io.flutter.plugin.common.BinaryMessenger; | ||
import io.flutter.plugin.common.MethodCall; | ||
import io.flutter.plugin.common.MethodChannel; | ||
import io.flutter.plugin.common.StandardMethodCodec; | ||
import java.nio.ByteBuffer; | ||
import java.util.HashMap; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.ArgumentCaptor; | ||
import org.robolectric.annotation.Config; | ||
|
||
@Config(manifest = Config.NONE) | ||
@RunWith(AndroidJUnit4.class) | ||
public class KeyboardChannelTest { | ||
|
||
private static BinaryMessenger.BinaryReply sendToBinaryMessageHandler( | ||
BinaryMessenger.BinaryMessageHandler binaryMessageHandler, String method, Object args) { | ||
MethodCall methodCall = new MethodCall(method, args); | ||
ByteBuffer encodedMethodCall = StandardMethodCodec.INSTANCE.encodeMethodCall(methodCall); | ||
BinaryMessenger.BinaryReply reply = mock(BinaryMessenger.BinaryReply.class); | ||
binaryMessageHandler.onMessage((ByteBuffer) encodedMethodCall.flip(), reply); | ||
return reply; | ||
} | ||
|
||
@Test | ||
public void respondsToGetKeyboardStateChannelMessage() { | ||
ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> binaryMessageHandlerCaptor = | ||
ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class); | ||
DartExecutor mockBinaryMessenger = mock(DartExecutor.class); | ||
KeyboardChannel.KeyboardMethodHandler mockHandler = | ||
mock(KeyboardChannel.KeyboardMethodHandler.class); | ||
KeyboardChannel keyboardChannel = new KeyboardChannel(mockBinaryMessenger); | ||
|
||
verify(mockBinaryMessenger, times(1)) | ||
.setMessageHandler(any(String.class), binaryMessageHandlerCaptor.capture()); | ||
|
||
BinaryMessenger.BinaryMessageHandler binaryMessageHandler = | ||
binaryMessageHandlerCaptor.getValue(); | ||
|
||
keyboardChannel.setKeyboardMethodHandler(mockHandler); | ||
sendToBinaryMessageHandler(binaryMessageHandler, "getKeyboardState", null); | ||
|
||
verify(mockHandler, times(1)).getKeyboardState(); | ||
} | ||
|
||
@Test | ||
public void repliesWhenNoKeyboardChannelHandler() { | ||
// Regression test for https://github.com/flutter/flutter/issues/122441#issuecomment-1582052616. | ||
|
||
KeyboardChannel keyboardChannel = new KeyboardChannel(mock(DartExecutor.class)); | ||
MethodCall methodCall = new MethodCall("getKeyboardState", null); | ||
MethodChannel.Result result = mock(MethodChannel.Result.class); | ||
keyboardChannel.parsingMethodHandler.onMethodCall(methodCall, result); | ||
|
||
verify(result).success(new HashMap()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters