Scribe (Android stylus handwriting text input) (flutter/engine#52943)

The engine API for Android's Scribe stylus handwriting input. Just bare bones handwriting itself, does not support special gestures, which will come in subsequent PR(s).
This commit is contained in:
Justin McCandless 2024-10-21 11:55:02 -07:00 committed by GitHub
parent b21f22cc50
commit cd1cd27685
16 changed files with 851 additions and 39 deletions

View File

@ -44294,6 +44294,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/syst
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ScribeChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java + ../../../flutter/LICENSE
@ -44317,6 +44318,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/Flutte
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ListenableEditingState.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ScribePlugin.java + ../../../flutter/LICENSE
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
@ -47176,6 +47178,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/system
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/RestorationChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/ScribeChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java
@ -47202,6 +47205,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterT
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ListenableEditingState.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ScribePlugin.java
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

View File

@ -294,6 +294,7 @@ android_java_sources = [
"io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java",
"io/flutter/embedding/engine/systemchannels/ProcessTextChannel.java",
"io/flutter/embedding/engine/systemchannels/RestorationChannel.java",
"io/flutter/embedding/engine/systemchannels/ScribeChannel.java",
"io/flutter/embedding/engine/systemchannels/SettingsChannel.java",
"io/flutter/embedding/engine/systemchannels/SpellCheckChannel.java",
"io/flutter/embedding/engine/systemchannels/SystemChannel.java",
@ -320,6 +321,7 @@ android_java_sources = [
"io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java",
"io/flutter/plugin/editing/InputConnectionAdaptor.java",
"io/flutter/plugin/editing/ListenableEditingState.java",
"io/flutter/plugin/editing/ScribePlugin.java",
"io/flutter/plugin/editing/SpellCheckPlugin.java",
"io/flutter/plugin/editing/TextEditingDelta.java",
"io/flutter/plugin/editing/TextInputPlugin.java",

View File

@ -63,6 +63,7 @@ import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.editing.ScribePlugin;
import io.flutter.plugin.editing.SpellCheckPlugin;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.localization.LocalizationPlugin;
@ -133,6 +134,7 @@ public class FlutterView extends FrameLayout
@Nullable private MouseCursorPlugin mouseCursorPlugin;
@Nullable private TextInputPlugin textInputPlugin;
@Nullable private SpellCheckPlugin spellCheckPlugin;
@Nullable private ScribePlugin scribePlugin;
@Nullable private LocalizationPlugin localizationPlugin;
@Nullable private KeyboardManager keyboardManager;
@Nullable private AndroidTouchProcessor androidTouchProcessor;
@ -1120,10 +1122,12 @@ public class FlutterView extends FrameLayout
if (Build.VERSION.SDK_INT >= API_LEVELS.API_24) {
mouseCursorPlugin = new MouseCursorPlugin(this, this.flutterEngine.getMouseCursorChannel());
}
textInputPlugin =
new TextInputPlugin(
this,
this.flutterEngine.getTextInputChannel(),
this.flutterEngine.getScribeChannel(),
this.flutterEngine.getPlatformViewsController());
try {
@ -1136,6 +1140,10 @@ public class FlutterView extends FrameLayout
Log.e(TAG, "TextServicesManager not supported by device, spell check disabled.");
}
scribePlugin =
new ScribePlugin(
this, textInputPlugin.getInputMethodManager(), this.flutterEngine.getScribeChannel());
localizationPlugin = this.flutterEngine.getLocalizationPlugin();
keyboardManager = new KeyboardManager(this);

View File

@ -34,6 +34,7 @@ import io.flutter.embedding.engine.systemchannels.NavigationChannel;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.embedding.engine.systemchannels.ProcessTextChannel;
import io.flutter.embedding.engine.systemchannels.RestorationChannel;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.embedding.engine.systemchannels.SpellCheckChannel;
import io.flutter.embedding.engine.systemchannels.SystemChannel;
@ -100,6 +101,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater {
@NonNull private final RestorationChannel restorationChannel;
@NonNull private final PlatformChannel platformChannel;
@NonNull private final ProcessTextChannel processTextChannel;
@NonNull private final ScribeChannel scribeChannel;
@NonNull private final SettingsChannel settingsChannel;
@NonNull private final SpellCheckChannel spellCheckChannel;
@NonNull private final SystemChannel systemChannel;
@ -337,6 +339,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater {
platformChannel = new PlatformChannel(dartExecutor);
processTextChannel = new ProcessTextChannel(dartExecutor, context.getPackageManager());
restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
scribeChannel = new ScribeChannel(dartExecutor);
settingsChannel = new SettingsChannel(dartExecutor);
spellCheckChannel = new SpellCheckChannel(dartExecutor);
systemChannel = new SystemChannel(dartExecutor);
@ -610,6 +613,12 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater {
return textInputChannel;
}
/** System channel that sends and receives Scribe requests and results. */
@NonNull
public ScribeChannel getScribeChannel() {
return scribeChannel;
}
/** System channel that sends and receives spell check requests and results. */
@NonNull
public SpellCheckChannel getSpellCheckChannel() {

View File

@ -0,0 +1,147 @@
// 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 static io.flutter.Build.API_LEVELS;
import android.annotation.TargetApi;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import io.flutter.Log;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.JSONMethodCodec;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
/**
* {@link ScribeChannel} is a platform channel that is used by the framework to facilitate the
* Scribe handwriting text input feature.
*/
public class ScribeChannel {
private static final String TAG = "ScribeChannel";
@VisibleForTesting
public static final String METHOD_IS_FEATURE_AVAILABLE = "Scribe.isFeatureAvailable";
@VisibleForTesting
public static final String METHOD_IS_STYLUS_HANDWRITING_AVAILABLE =
"Scribe.isStylusHandwritingAvailable";
@VisibleForTesting
public static final String METHOD_START_STYLUS_HANDWRITING = "Scribe.startStylusHandwriting";
public final MethodChannel channel;
private ScribeMethodHandler scribeMethodHandler;
@NonNull
public final MethodChannel.MethodCallHandler parsingMethodHandler =
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (scribeMethodHandler == null) {
Log.v(TAG, "No ScribeMethodHandler registered. Scribe call not handled.");
return;
}
String method = call.method;
Log.v(TAG, "Received '" + method + "' message.");
switch (method) {
case METHOD_IS_FEATURE_AVAILABLE:
isFeatureAvailable(call, result);
break;
case METHOD_IS_STYLUS_HANDWRITING_AVAILABLE:
isStylusHandwritingAvailable(call, result);
break;
case METHOD_START_STYLUS_HANDWRITING:
startStylusHandwriting(call, result);
break;
default:
result.notImplemented();
break;
}
}
};
private void isFeatureAvailable(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
try {
final boolean isAvailable = scribeMethodHandler.isFeatureAvailable();
result.success(isAvailable);
} catch (IllegalStateException exception) {
result.error("error", exception.getMessage(), null);
}
}
private void isStylusHandwritingAvailable(
@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (Build.VERSION.SDK_INT < API_LEVELS.API_34) {
result.error("error", "Requires API level 34 or higher.", null);
return;
}
try {
final boolean isAvailable = scribeMethodHandler.isStylusHandwritingAvailable();
result.success(isAvailable);
} catch (IllegalStateException exception) {
result.error("error", exception.getMessage(), null);
}
}
private void startStylusHandwriting(
@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (Build.VERSION.SDK_INT < API_LEVELS.API_33) {
result.error("error", "Requires API level 33 or higher.", null);
return;
}
try {
scribeMethodHandler.startStylusHandwriting();
result.success(null);
} catch (IllegalStateException exception) {
result.error("error", exception.getMessage(), null);
}
}
public ScribeChannel(@NonNull DartExecutor dartExecutor) {
channel = new MethodChannel(dartExecutor, "flutter/scribe", JSONMethodCodec.INSTANCE);
channel.setMethodCallHandler(parsingMethodHandler);
}
/**
* Sets the {@link ScribeMethodHandler} which receives all requests for scribe sent through this
* channel.
*/
public void setScribeMethodHandler(@Nullable ScribeMethodHandler scribeMethodHandler) {
this.scribeMethodHandler = scribeMethodHandler;
}
public interface ScribeMethodHandler {
/**
* Responds to the {@code result} with success and a boolean indicating whether or not stylus
* handwriting is available.
*/
boolean isFeatureAvailable();
/**
* Responds to the {@code result} with success and a boolean indicating whether or not stylus
* handwriting is available.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
boolean isStylusHandwritingAvailable();
/**
* Requests to start Scribe stylus handwriting, which will respond to the {@code result} with
* either success if handwriting input has started or error otherwise.
*/
@TargetApi(API_LEVELS.API_33)
@RequiresApi(API_LEVELS.API_33)
void startStylusHandwriting();
}
// TODO(justinmc): Scribe stylus gestures should be supported here.
// https://github.com/flutter/flutter/issues/156018
}

View File

@ -33,6 +33,7 @@ import androidx.annotation.RequiresApi;
import androidx.core.view.inputmethod.InputConnectionCompat;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
@ -51,6 +52,7 @@ public class InputConnectionAdaptor extends BaseInputConnection
private final View mFlutterView;
private final int mClient;
private final ScribeChannel scribeChannel;
private final TextInputChannel textInputChannel;
private final ListenableEditingState mEditable;
private final EditorInfo mEditorInfo;
@ -69,6 +71,7 @@ public class InputConnectionAdaptor extends BaseInputConnection
View view,
int client,
TextInputChannel textInputChannel,
ScribeChannel scribeChannel,
KeyboardDelegate keyboardDelegate,
ListenableEditingState editable,
EditorInfo editorInfo,
@ -77,6 +80,7 @@ public class InputConnectionAdaptor extends BaseInputConnection
mFlutterView = view;
mClient = client;
this.textInputChannel = textInputChannel;
this.scribeChannel = scribeChannel;
mEditable = editable;
mEditable.addEditingStateListener(this);
mEditorInfo = editorInfo;
@ -100,10 +104,19 @@ public class InputConnectionAdaptor extends BaseInputConnection
View view,
int client,
TextInputChannel textInputChannel,
ScribeChannel scribeChannel,
KeyboardDelegate keyboardDelegate,
ListenableEditingState editable,
EditorInfo editorInfo) {
this(view, client, textInputChannel, keyboardDelegate, editable, editorInfo, new FlutterJNI());
this(
view,
client,
textInputChannel,
scribeChannel,
keyboardDelegate,
editable,
editorInfo,
new FlutterJNI());
}
private ExtractedText getExtractedText(ExtractedTextRequest request) {
@ -262,6 +275,10 @@ public class InputConnectionAdaptor extends BaseInputConnection
return result;
}
// TODO(justinmc): Scribe stylus gestures should be supported here via
// performHandwritingGesture.
// https://github.com/flutter/flutter/issues/156018
// Sanitizes the index to ensure the index is within the range of the
// contents of editable.
private static int clampIndexToEditable(int index, Editable editable) {

View File

@ -0,0 +1,106 @@
// 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.editing;
import static io.flutter.Build.API_LEVELS;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
/**
* {@link ScribePlugin} is the implementation of all functionality needed for handwriting stylus
* text input.
*
* <p>The plugin handles requests for scribe sent by the {@link
* io.flutter.embedding.engine.systemchannels.ScribeChannel}.
*
* <p>On API versions below 33, the plugin does nothing.
*/
public class ScribePlugin implements ScribeChannel.ScribeMethodHandler {
@NonNull private final ScribeChannel mScribeChannel;
@NonNull private final InputMethodManager mInputMethodManager;
@NonNull private View mView;
public ScribePlugin(
@NonNull View view, @NonNull InputMethodManager imm, @NonNull ScribeChannel scribeChannel) {
if (Build.VERSION.SDK_INT >= API_LEVELS.API_33) {
view.setAutoHandwritingEnabled(false);
}
mView = view;
mInputMethodManager = imm;
mScribeChannel = scribeChannel;
mScribeChannel.setScribeMethodHandler(this);
}
/**
* Sets the View in which Scribe input is handled.
*
* <p>Only one View can be set at any given time.
*/
public void setView(@NonNull View view) {
if (view == mView) {
return;
}
mView = view;
}
/**
* Unregisters this {@code ScribePlugin} as the {@code ScribeChannel.ScribeMethodHandler}, for the
* {@link io.flutter.embedding.engine.systemchannels.ScribeChannel}.
*
* <p>Do not invoke any methods on a {@code ScribePlugin} after invoking this method.
*/
public void destroy() {
mScribeChannel.setScribeMethodHandler(null);
}
/**
* Returns true if the InputMethodManager supports Scribe stylus handwriting input.
*
* <p>Call this or isFeatureAvailable before calling startStylusHandwriting to make sure it's
* available.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
@Override
public boolean isStylusHandwritingAvailable() {
return mInputMethodManager.isStylusHandwritingAvailable();
}
/**
* Starts stylus handwriting input.
*
* <p>Typically isStylusHandwritingAvailable should be called first to determine whether this is
* supported by the IME.
*/
@TargetApi(API_LEVELS.API_33)
@RequiresApi(API_LEVELS.API_33)
@Override
public void startStylusHandwriting() {
mInputMethodManager.startStylusHandwriting(mView);
}
/**
* A convenience method to check if Scribe is available.
*
* <p>Differs from isStylusHandwritingAvailable in that it can be called from any API level
* without throwing an error.
*
* <p>Call this or isStylusHandwritingAvailable before calling startStylusHandwriting to make sure
* it's available.
*/
@Override
public boolean isFeatureAvailable() {
return Build.VERSION.SDK_INT >= API_LEVELS.API_34 && isStylusHandwritingAvailable();
}
}

View File

@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.view.inputmethod.EditorInfoCompat;
import io.flutter.Log;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel.TextEditState;
import io.flutter.plugin.platform.PlatformViewsController;
@ -42,6 +43,7 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
@NonNull private final View mView;
@NonNull private final InputMethodManager mImm;
@NonNull private final AutofillManager afm;
@NonNull private final ScribeChannel scribeChannel;
@NonNull private final TextInputChannel textInputChannel;
@NonNull private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
@Nullable private TextInputChannel.Configuration configuration;
@ -66,6 +68,7 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
public TextInputPlugin(
@NonNull View view,
@NonNull TextInputChannel textInputChannel,
@NonNull ScribeChannel scribeChannel,
@NonNull PlatformViewsController platformViewsController) {
mView = view;
// Create a default object.
@ -153,6 +156,8 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
textInputChannel.requestExistingInputState();
this.scribeChannel = scribeChannel;
this.platformViewsController = platformViewsController;
this.platformViewsController.attachTextInputPlugin(this);
}
@ -341,9 +346,23 @@ public class TextInputPlugin implements ListenableEditingState.EditingStateWatch
EditorInfoCompat.setContentMimeTypes(outAttrs, imgTypeString);
}
if (Build.VERSION.SDK_INT >= API_LEVELS.API_34) {
EditorInfoCompat.setStylusHandwritingEnabled(outAttrs, true);
}
// TODO(justinmc): Scribe stylus gestures should be supported here via
// outAttrs.setSupportedHandwritingGestures and
// outAttrs.setSupportedHandwritingGesturePreviews.
// https://github.com/flutter/flutter/issues/156018
InputConnectionAdaptor connection =
new InputConnectionAdaptor(
view, inputTarget.id, textInputChannel, keyboardManager, mEditable, outAttrs);
view,
inputTarget.id,
textInputChannel,
scribeChannel,
keyboardManager,
mEditable,
outAttrs);
outAttrs.initialSelStart = mEditable.getSelectionStart();
outAttrs.initialSelEnd = mEditable.getSelectionEnd();

View File

@ -56,6 +56,7 @@ import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
import io.flutter.embedding.engine.systemchannels.NavigationChannel;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.embedding.engine.systemchannels.SystemChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
@ -237,7 +238,11 @@ public class FlutterView extends SurfaceView
PlatformViewsController platformViewsController =
mNativeView.getPluginRegistry().getPlatformViewsController();
mTextInputPlugin =
new TextInputPlugin(this, new TextInputChannel(dartExecutor), platformViewsController);
new TextInputPlugin(
this,
new TextInputChannel(dartExecutor),
new ScribeChannel(dartExecutor),
platformViewsController);
mKeyboardManager = new KeyboardManager(this);
if (Build.VERSION.SDK_INT >= API_LEVELS.API_24) {

View File

@ -49,6 +49,7 @@ import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
import io.flutter.embedding.engine.systemchannels.NavigationChannel;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.embedding.engine.systemchannels.SystemChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
@ -1481,6 +1482,7 @@ public class FlutterActivityAndFragmentDelegateTest {
when(engine.getSettingsChannel()).thenReturn(fakeSettingsChannel);
when(engine.getSystemChannel()).thenReturn(mock(SystemChannel.class));
when(engine.getTextInputChannel()).thenReturn(mock(TextInputChannel.class));
when(engine.getScribeChannel()).thenReturn(mock(ScribeChannel.class));
return engine;
}

View File

@ -0,0 +1,202 @@
// 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 static io.flutter.Build.API_LEVELS;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.annotation.TargetApi;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.FlutterException;
import io.flutter.plugin.common.JSONMethodCodec;
import io.flutter.plugin.common.MethodCall;
import java.nio.ByteBuffer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.annotation.Config;
@RunWith(AndroidJUnit4.class)
public class ScribeChannelTest {
private static BinaryMessenger.BinaryReply sendToBinaryMessageHandler(
BinaryMessenger.BinaryMessageHandler binaryMessageHandler, String method) {
MethodCall methodCall = new MethodCall(method, null);
ByteBuffer encodedMethodCall = JSONMethodCodec.INSTANCE.encodeMethodCall(methodCall);
BinaryMessenger.BinaryReply mockReply = mock(BinaryMessenger.BinaryReply.class);
binaryMessageHandler.onMessage((ByteBuffer) encodedMethodCall.flip(), mockReply);
return mockReply;
}
ScribeChannel.ScribeMethodHandler mockHandler;
BinaryMessenger.BinaryMessageHandler binaryMessageHandler;
@Before
public void setUp() {
ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> binaryMessageHandlerCaptor =
ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
DartExecutor mockBinaryMessenger = mock(DartExecutor.class);
mockHandler = mock(ScribeChannel.ScribeMethodHandler.class);
ScribeChannel scribeChannel = new ScribeChannel(mockBinaryMessenger);
scribeChannel.setScribeMethodHandler(mockHandler);
verify((BinaryMessenger) mockBinaryMessenger, times(1))
.setMessageHandler(any(String.class), binaryMessageHandlerCaptor.capture());
binaryMessageHandler = binaryMessageHandlerCaptor.getValue();
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void respondsToStartStylusHandwriting() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(
binaryMessageHandler, ScribeChannel.METHOD_START_STYLUS_HANDWRITING);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
return decodedReply == null;
} catch (FlutterException e) {
return false;
}
}));
verify(mockHandler).startStylusHandwriting();
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void respondsToIsFeatureAvailable() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(binaryMessageHandler, ScribeChannel.METHOD_IS_FEATURE_AVAILABLE);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
// Should succeed and should tell whether or not Scribe is available by
// using a boolean.
return decodedReply.getClass() == java.lang.Boolean.class;
} catch (FlutterException e) {
return false;
}
}));
verify(mockHandler).isFeatureAvailable();
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void respondsToIsStylusHandwritingAvailable() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(
binaryMessageHandler, ScribeChannel.METHOD_IS_STYLUS_HANDWRITING_AVAILABLE);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
// Should succeed and should tell whether or not Scribe is available by
// using a boolean.
return decodedReply.getClass() == java.lang.Boolean.class;
} catch (FlutterException e) {
return false;
}
}));
verify(mockHandler).isStylusHandwritingAvailable();
}
@Config(sdk = API_LEVELS.API_32)
@TargetApi(API_LEVELS.API_32)
@Test
public void respondsToStartStylusHandwritingWhenAPILevelUnsupported() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(
binaryMessageHandler, ScribeChannel.METHOD_START_STYLUS_HANDWRITING);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
return false;
} catch (FlutterException e) {
// Should fail because the API version is too low.
return true;
}
}));
verify(mockHandler, never()).startStylusHandwriting();
}
@Config(sdk = API_LEVELS.API_33)
@TargetApi(API_LEVELS.API_33)
@Test
public void respondsToIsFeatureAvailableWhenAPILevelUnsupported() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(binaryMessageHandler, ScribeChannel.METHOD_IS_FEATURE_AVAILABLE);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
// Should succeed and indicate that Scribe is not available.
return decodedReply.getClass() == java.lang.Boolean.class
&& !((boolean) decodedReply);
} catch (FlutterException e) {
return false;
}
}));
verify(mockHandler).isFeatureAvailable();
}
@Config(sdk = API_LEVELS.API_33)
@TargetApi(API_LEVELS.API_33)
@Test
public void respondsToIsStylusHandwritingAvailableWhenAPILevelUnsupported() {
BinaryMessenger.BinaryReply mockReply =
sendToBinaryMessageHandler(
binaryMessageHandler, ScribeChannel.METHOD_IS_STYLUS_HANDWRITING_AVAILABLE);
verify(mockReply)
.reply(
argThat(
(ByteBuffer reply) -> {
reply.flip();
try {
final Object decodedReply = JSONMethodCodec.INSTANCE.decodeEnvelope(reply);
return false;
} catch (FlutterException e) {
// Should fail because the API version is too low.
return true;
}
}));
verify(mockHandler, never()).isStylusHandwritingAvailable();
}
}

View File

@ -43,6 +43,7 @@ import com.ibm.icu.lang.UProperty;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.common.JSONMethodCodec;
import io.flutter.plugin.common.MethodCall;
@ -105,6 +106,7 @@ public class InputConnectionAdaptorTest {
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
int inputTargetId = 0;
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState mEditable = new ListenableEditingState(null, testView);
Selection.setSelection(mEditable, 0, 0);
ListenableEditingState spyEditable = spy(mEditable);
@ -113,7 +115,13 @@ public class InputConnectionAdaptorTest {
InputConnectionAdaptor inputConnectionAdaptor =
new InputConnectionAdaptor(
testView, inputTargetId, textInputChannel, mockKeyboardManager, spyEditable, outAttrs);
testView,
inputTargetId,
textInputChannel,
scribeChannel,
mockKeyboardManager,
spyEditable,
outAttrs);
// Send an enter key and make sure the Editable received it.
FakeKeyEvent keyEvent = new FakeKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, '\n');
@ -199,12 +207,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -260,12 +270,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -291,12 +303,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -328,12 +342,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -363,12 +379,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -401,12 +419,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -436,12 +456,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -475,12 +497,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -512,12 +536,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -547,12 +573,14 @@ public class InputConnectionAdaptorTest {
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJNI, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(dartExecutor);
ListenableEditingState editable = sampleEditable(0, 0);
InputConnectionAdaptor adaptor =
new InputConnectionAdaptor(
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
@ -1080,6 +1108,7 @@ public class InputConnectionAdaptorTest {
testView,
1,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editable,
new EditorInfo());
@ -1131,6 +1160,7 @@ public class InputConnectionAdaptorTest {
testView,
1,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editable,
new EditorInfo());
@ -1277,6 +1307,7 @@ public class InputConnectionAdaptorTest {
View testView = new View(ApplicationProvider.getApplicationContext());
int client = 0;
TextInputChannel textInputChannel = mock(TextInputChannel.class);
ScribeChannel scribeChannel = mock(ScribeChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
when(mockFlutterJNI.isCodePointEmoji(anyInt()))
.thenAnswer((invocation) -> Emoji.isEmoji((int) invocation.getArguments()[0]));
@ -1290,7 +1321,14 @@ public class InputConnectionAdaptorTest {
.thenAnswer(
(invocation) -> Emoji.isRegionalIndicatorSymbol((int) invocation.getArguments()[0]));
return new InputConnectionAdaptor(
testView, client, textInputChannel, mockKeyboardManager, editable, null, mockFlutterJNI);
testView,
client,
textInputChannel,
scribeChannel,
mockKeyboardManager,
editable,
null,
mockFlutterJNI);
}
private static class Emoji {

View File

@ -14,6 +14,7 @@ import android.view.inputmethod.EditorInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.android.KeyboardManager;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import java.util.ArrayList;
import org.junit.Before;
@ -280,6 +281,7 @@ public class ListenableEditingStateTest {
testView,
0,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editingState,
new EditorInfo());
@ -305,6 +307,7 @@ public class ListenableEditingStateTest {
testView,
0,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editingState,
new EditorInfo());
@ -339,6 +342,7 @@ public class ListenableEditingStateTest {
testView,
0,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editingState,
new EditorInfo());
@ -399,6 +403,7 @@ public class ListenableEditingStateTest {
testView,
0,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
editingState,
new EditorInfo());

View File

@ -0,0 +1,106 @@
// 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.editing;
import static io.flutter.Build.API_LEVELS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@RunWith(AndroidJUnit4.class)
public class ScribePluginTest {
private final Context ctx = ApplicationProvider.getApplicationContext();
ScribePlugin scribePlugin;
InputMethodManager mockImm;
View testView;
@Before
public void setUp() {
ScribeChannel mockScribeChannel = mock(ScribeChannel.class);
testView = new View(ctx);
mockImm = mock(InputMethodManager.class);
if (Build.VERSION.SDK_INT >= API_LEVELS.API_34) {
when(mockImm.isStylusHandwritingAvailable()).thenReturn(true);
}
scribePlugin = new ScribePlugin(testView, mockImm, mockScribeChannel);
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void scribePluginIsFeatureAvailable() {
assertEquals(scribePlugin.isFeatureAvailable(), true);
verify(mockImm).isStylusHandwritingAvailable();
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void scribePluginIsStylusHandwritingAvailable() {
assertEquals(scribePlugin.isStylusHandwritingAvailable(), true);
verify(mockImm).isStylusHandwritingAvailable();
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void scribePluginStartStylusHandwriting() {
scribePlugin.startStylusHandwriting();
verify(mockImm).startStylusHandwriting(testView);
}
@Config(sdk = API_LEVELS.API_32)
@TargetApi(API_LEVELS.API_32)
@Test
public void scribePluginStartStylusHandwritingWhenAPILevelUnsupported() {
assertNotNull(scribePlugin);
assertThrows(
NoSuchMethodError.class,
() -> {
scribePlugin.startStylusHandwriting();
});
}
@Config(sdk = API_LEVELS.API_33)
@TargetApi(API_LEVELS.API_33)
@Test
public void scribePluginIsFeatureAvailableWhenAPILevelUnsupported() {
assertEquals(scribePlugin.isFeatureAvailable(), false);
}
@Config(sdk = API_LEVELS.API_33)
@TargetApi(API_LEVELS.API_33)
@Test
public void scribePluginIsStylusHandwritingAvailableWhenAPILevelUnsupported() {
assertNotNull(scribePlugin);
assertThrows(
NoSuchMethodError.class,
() -> {
scribePlugin.isStylusHandwritingAvailable();
});
}
}

View File

@ -2,6 +2,7 @@ package io.flutter.plugin.editing;
import static io.flutter.Build.API_LEVELS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@ -45,6 +46,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import androidx.core.view.inputmethod.EditorInfoCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import io.flutter.embedding.android.FlutterView;
@ -54,6 +56,7 @@ import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel.TextEditState;
import io.flutter.plugin.common.BinaryMessenger;
@ -133,8 +136,10 @@ public class TextInputPluginTest {
FlutterJNI mockFlutterJni = mock(FlutterJNI.class);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
ArgumentCaptor<String> channelCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<ByteBuffer> bufferCaptor = ArgumentCaptor.forClass(ByteBuffer.class);
@ -152,8 +157,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -194,8 +201,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Here's no textInputPlugin.setTextInputClient()
textInputPlugin.setTextInputEditingState(
@ -211,8 +220,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -261,8 +272,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
CharSequence newText = "I do not fear computers. I fear the lack of them.";
// Change InputTarget to FRAMEWORK_CLIENT.
@ -374,8 +387,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
CharSequence newText = "I do not fear computers. I fear the lack of them.";
final TextEditingDelta expectedDelta =
new TextEditingDelta("", 0, 0, newText, newText.length(), newText.length(), 0, 49);
@ -503,8 +518,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
CharSequence newText = "I do not fear computers. I fear the lack of them.";
final TextEditingDelta expectedDelta =
new TextEditingDelta("", 0, 0, newText, newText.length(), newText.length(), 0, 49);
@ -612,8 +629,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
CharSequence newText = "I do not fear computers. I fear the lack of them.";
final TextEditingDelta expectedDelta =
new TextEditingDelta(
@ -722,8 +741,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
CharSequence newText = "helfo";
final TextEditingDelta expectedDelta = new TextEditingDelta(newText, 0, 5, "hello", 5, 5, 0, 5);
@ -829,8 +850,10 @@ public class TextInputPluginTest {
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Change InputTarget to FRAMEWORK_CLIENT.
textInputPlugin.setTextInputClient(
@ -920,8 +943,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -958,8 +983,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1006,8 +1033,10 @@ public class TextInputPluginTest {
testImm.setCurrentInputMethodSubtype(inputMethodSubtype);
View testView = new View(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1107,8 +1136,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1134,8 +1165,10 @@ public class TextInputPluginTest {
public void destroy_clearTextInputMethodHandler() {
View testView = new View(ctx);
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
verify(textInputChannel, times(1)).setTextInputMethodHandler(isNotNull());
textInputPlugin.destroy();
verify(textInputChannel, times(1)).setTextInputMethodHandler(isNull());
@ -1150,8 +1183,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1225,8 +1260,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class)));
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1263,8 +1300,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
DartExecutor dartExecutor = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1293,8 +1332,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
DartExecutor dartExecutor = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1321,8 +1362,10 @@ public class TextInputPluginTest {
View testView = new View(ctx);
DartExecutor dartExecutor = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
@ -1351,6 +1394,74 @@ public class TextInputPluginTest {
| InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
}
@Config(minSdk = API_LEVELS.API_34)
@TargetApi(API_LEVELS.API_34)
@Test
public void inputConnection_setsStylusHandwritingAvailable() {
View testView = new View(ctx);
DartExecutor dartExecutor = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
false,
false,
true,
true,
false,
TextInputChannel.TextCapitalization.NONE,
new TextInputChannel.InputType(TextInputChannel.TextInputType.MULTILINE, false, false),
null,
null,
null,
null,
null));
EditorInfo editorInfo = new EditorInfo();
InputConnection connection =
textInputPlugin.createInputConnection(testView, mock(KeyboardManager.class), editorInfo);
assertTrue(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
}
@Config(sdk = API_LEVELS.API_32)
@TargetApi(API_LEVELS.API_32)
@Test
public void inputConnection_doesNotcallSetsStylusHandwritingAvailableWhenAPILevelUnsupported() {
View testView = new View(ctx);
DartExecutor dartExecutor = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
textInputPlugin.setTextInputClient(
0,
new TextInputChannel.Configuration(
false,
false,
true,
true,
false,
TextInputChannel.TextCapitalization.NONE,
new TextInputChannel.InputType(TextInputChannel.TextInputType.MULTILINE, false, false),
null,
null,
null,
null,
null));
EditorInfo editorInfo = new EditorInfo();
InputConnection connection =
textInputPlugin.createInputConnection(testView, mock(KeyboardManager.class), editorInfo);
assertFalse(EditorInfoCompat.isStylusHandwritingEnabled(editorInfo));
}
// -------- Start: Autofill Tests -------
@Test
public void autofill_enabledByDefault() {
@ -1359,8 +1470,10 @@ public class TextInputPluginTest {
}
FlutterView testView = new FlutterView(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
final TextInputChannel.Configuration.Autofill autofill =
new TextInputChannel.Configuration.Autofill(
"1", new String[] {}, null, new TextInputChannel.TextEditState("", 0, 0, -1, -1));
@ -1419,8 +1532,10 @@ public class TextInputPluginTest {
}
FlutterView testView = new FlutterView(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
final TextInputChannel.Configuration.Autofill autofill =
new TextInputChannel.Configuration.Autofill(
"1", new String[] {}, null, new TextInputChannel.TextEditState("", 0, 0, -1, -1));
@ -1456,8 +1571,10 @@ public class TextInputPluginTest {
}
FlutterView testView = new FlutterView(ctx);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
final TextInputChannel.Configuration.Autofill autofill =
new TextInputChannel.Configuration.Autofill(
"1",
@ -1501,8 +1618,10 @@ public class TextInputPluginTest {
}
FlutterView testView = getTestView();
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
final TextInputChannel.Configuration.Autofill autofill1 =
new TextInputChannel.Configuration.Autofill(
"1",
@ -1593,8 +1712,10 @@ public class TextInputPluginTest {
// Migrate to ActivityScenario by following https://github.com/robolectric/robolectric/pull/4736
FlutterView testView = getTestView();
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
final TextInputChannel.Configuration.Autofill autofill =
new TextInputChannel.Configuration.Autofill(
"1",
@ -1646,8 +1767,10 @@ public class TextInputPluginTest {
TestAfm testAfm = Shadow.extract(ctx.getSystemService(AutofillManager.class));
FlutterView testView = getTestView();
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Set up an autofill scenario with 2 fields.
final TextInputChannel.Configuration.Autofill autofill1 =
@ -1728,6 +1851,7 @@ public class TextInputPluginTest {
testView,
0,
mock(TextInputChannel.class),
mock(ScribeChannel.class),
mockKeyboardManager,
(ListenableEditingState) textInputPlugin.getEditable(),
new EditorInfo());
@ -1782,8 +1906,10 @@ public class TextInputPluginTest {
TestAfm testAfm = Shadow.extract(ctx.getSystemService(AutofillManager.class));
FlutterView testView = getTestView();
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Set up an autofill scenario with 2 fields.
final TextInputChannel.Configuration.Autofill autofill1 =
@ -1877,8 +2003,10 @@ public class TextInputPluginTest {
}
FlutterView testView = new FlutterView(ctx);
TextInputChannel textInputChannel = spy(new TextInputChannel(mock(DartExecutor.class)));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Set up an autofill scenario with 2 fields.
final TextInputChannel.Configuration.Autofill autofillConfig =
new TextInputChannel.Configuration.Autofill(
@ -1928,8 +2056,10 @@ public class TextInputPluginTest {
TestAfm testAfm = Shadow.extract(ctx.getSystemService(AutofillManager.class));
FlutterView testView = getTestView();
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
// Set up an autofill scenario with 2 fields.
final TextInputChannel.Configuration.Autofill autofill1 =
@ -2041,6 +2171,7 @@ public class TextInputPluginTest {
ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
DartExecutor mockBinaryMessenger = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(mockBinaryMessenger);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
EventHandler mockEventHandler = mock(EventHandler.class);
TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE));
@ -2048,7 +2179,8 @@ public class TextInputPluginTest {
View testView = new View(ctx);
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
verify(mockBinaryMessenger, times(1))
.setMessageHandler(any(String.class), binaryMessageHandlerCaptor.capture());
@ -2072,6 +2204,7 @@ public class TextInputPluginTest {
ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
DartExecutor mockBinaryMessenger = mock(DartExecutor.class);
TextInputChannel textInputChannel = new TextInputChannel(mockBinaryMessenger);
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
EventHandler mockEventHandler = mock(EventHandler.class);
TestImm testImm = Shadow.extract(ctx.getSystemService(Context.INPUT_METHOD_SERVICE));
@ -2079,7 +2212,8 @@ public class TextInputPluginTest {
View testView = new View(ctx);
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
verify(mockBinaryMessenger, times(1))
.setMessageHandler(any(String.class), binaryMessageHandlerCaptor.capture());
@ -2108,8 +2242,10 @@ public class TextInputPluginTest {
when(testView.getWindowSystemUiVisibility()).thenReturn(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
ImeSyncDeferringInsetsCallback imeSyncCallback = textInputPlugin.getImeSyncCallback();
FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni));
FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni));
@ -2189,8 +2325,10 @@ public class TextInputPluginTest {
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
ImeSyncDeferringInsetsCallback imeSyncCallback = textInputPlugin.getImeSyncCallback();
FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni));
FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni));
@ -2268,8 +2406,10 @@ public class TextInputPluginTest {
when(testView.getWindowSystemUiVisibility()).thenReturn(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class));
ScribeChannel scribeChannel = new ScribeChannel(mock(DartExecutor.class));
TextInputPlugin textInputPlugin =
new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class));
new TextInputPlugin(
testView, textInputChannel, scribeChannel, mock(PlatformViewsController.class));
ImeSyncDeferringInsetsCallback imeSyncCallback = textInputPlugin.getImeSyncCallback();
FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni));
FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni));

View File

@ -44,6 +44,7 @@ import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.MouseCursorChannel;
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel;
import io.flutter.embedding.engine.systemchannels.PlatformViewsChannel.PlatformViewTouch;
import io.flutter.embedding.engine.systemchannels.ScribeChannel;
import io.flutter.embedding.engine.systemchannels.SettingsChannel;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.common.MethodCall;
@ -1668,6 +1669,7 @@ public class PlatformViewsControllerTest {
when(engine.getMouseCursorChannel()).thenReturn(mock(MouseCursorChannel.class));
when(engine.getTextInputChannel()).thenReturn(mock(TextInputChannel.class));
when(engine.getSettingsChannel()).thenReturn(new SettingsChannel(executor));
when(engine.getScribeChannel()).thenReturn(mock(ScribeChannel.class));
when(engine.getPlatformViewsController()).thenReturn(platformViewsController);
when(engine.getLocalizationPlugin()).thenReturn(mock(LocalizationPlugin.class));
when(engine.getAccessibilityChannel()).thenReturn(mock(AccessibilityChannel.class));