mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Remove old flutter messaging API (#3482)
Breaking change: removed facilities for JSON and string messaging from FlutterView/FlutterViewController, leaving only binary messaging there. All other use of flutter communication now goes through FlutterMessageChannel and FlutterMethodChannels. Retained use of String and JSON codecs for now. Companion flutter PR: flutter/flutter#8837
This commit is contained in:
parent
4a5a324669
commit
c4edec7417
@ -82,18 +82,18 @@ android_library("java") {
|
||||
"io/flutter/app/FlutterActivity.java",
|
||||
"io/flutter/app/FlutterApplication.java",
|
||||
"io/flutter/plugin/common/ActivityLifecycleListener.java",
|
||||
"io/flutter/plugin/common/BinaryMessageCodec.java",
|
||||
"io/flutter/plugin/common/BinaryCodec.java",
|
||||
"io/flutter/plugin/common/FlutterException.java",
|
||||
"io/flutter/plugin/common/FlutterMessageChannel.java",
|
||||
"io/flutter/plugin/common/FlutterMethodChannel.java",
|
||||
"io/flutter/plugin/common/JSONMessageCodec.java",
|
||||
"io/flutter/plugin/common/JSONMessageListener.java",
|
||||
"io/flutter/plugin/common/JSONMethodCodec.java",
|
||||
"io/flutter/plugin/common/MessageCodec.java",
|
||||
"io/flutter/plugin/common/MethodCodec.java",
|
||||
"io/flutter/plugin/common/MethodCall.java",
|
||||
"io/flutter/plugin/common/StandardMessageCodec.java",
|
||||
"io/flutter/plugin/common/StandardMethodCodec.java",
|
||||
"io/flutter/plugin/common/StringMessageCodec.java",
|
||||
"io/flutter/plugin/common/StringCodec.java",
|
||||
"io/flutter/plugin/editing/InputConnectionAdaptor.java",
|
||||
"io/flutter/plugin/editing/TextInputPlugin.java",
|
||||
"io/flutter/plugin/platform/PlatformPlugin.java",
|
||||
|
||||
@ -9,11 +9,11 @@ import java.nio.ByteBuffer;
|
||||
/**
|
||||
* A {@link MessageCodec} using unencoded binary messages, represented as {@link ByteBuffer}s.
|
||||
*/
|
||||
public final class BinaryMessageCodec implements MessageCodec<ByteBuffer> {
|
||||
public final class BinaryCodec implements MessageCodec<ByteBuffer> {
|
||||
// This codec must match the Dart codec of the same name in package flutter/services.
|
||||
public static final BinaryMessageCodec INSTANCE = new BinaryMessageCodec();
|
||||
public static final BinaryCodec INSTANCE = new BinaryCodec();
|
||||
|
||||
private BinaryMessageCodec() {
|
||||
private BinaryCodec() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -0,0 +1,20 @@
|
||||
// Copyright 2017 The Chromium 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.common;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a Flutter method invocation failed on the Flutter side.
|
||||
*/
|
||||
public class FlutterException extends RuntimeException {
|
||||
public final String code;
|
||||
public final Object details;
|
||||
|
||||
FlutterException(String code, String message, Object details) {
|
||||
super(message);
|
||||
assert code != null;
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ package io.flutter.plugin.common;
|
||||
|
||||
import android.util.Log;
|
||||
import io.flutter.view.FlutterView;
|
||||
import io.flutter.view.FlutterView.BinaryMessageReplyCallback;
|
||||
import io.flutter.view.FlutterView.BinaryMessageResponse;
|
||||
import io.flutter.view.FlutterView.OnBinaryMessageListenerAsync;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -60,6 +61,27 @@ public final class FlutterMethodChannel {
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a method on this channel, expecting no result.
|
||||
*
|
||||
* @param method the name String of the method.
|
||||
* @param arguments the arguments for the invocation, possibly null.
|
||||
*/
|
||||
public void invokeMethod(String method, Object arguments) {
|
||||
invokeMethod(method, arguments, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a method on this channel.
|
||||
*
|
||||
* @param call a {@link MethodCall}.
|
||||
* @param handler a {@link Response} handler for the invocation result.
|
||||
*/
|
||||
public void invokeMethod(String method, Object arguments, Response handler) {
|
||||
view.sendBinaryMessage(name, codec.encodeMethodCall(new MethodCall(method, arguments)),
|
||||
handler == null ? null : new MethodCallResultCallback(handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a method call handler on this channel.
|
||||
*
|
||||
@ -106,7 +128,8 @@ public final class FlutterMethodChannel {
|
||||
* Handles a stream setup request.
|
||||
*
|
||||
* @param arguments Stream configuration arguments, possibly null.
|
||||
* @param eventSink A {@link EventSink} used to emit events once the stream has been set up.
|
||||
* @param eventSink An {@link EventSink} used to emit events once the stream has been set
|
||||
* up.
|
||||
*/
|
||||
void listen(Object arguments, EventSink eventSink);
|
||||
|
||||
@ -150,6 +173,24 @@ public final class FlutterMethodChannel {
|
||||
void done();
|
||||
}
|
||||
|
||||
private final class MethodCallResultCallback implements BinaryMessageReplyCallback {
|
||||
private final Response handler;
|
||||
|
||||
MethodCallResultCallback(Response handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReply(ByteBuffer reply) {
|
||||
try {
|
||||
final Object result = codec.decodeEnvelope(reply);
|
||||
handler.success(result);
|
||||
} catch (FlutterException e) {
|
||||
handler.error(e.code, e.getMessage(), e.details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class MethodCallListener implements OnBinaryMessageListenerAsync {
|
||||
private final MethodCallHandler handler;
|
||||
|
||||
@ -188,7 +229,7 @@ public final class FlutterMethodChannel {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG + name, "Failed to handle method call", e);
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(),null));
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,13 +277,13 @@ public final class FlutterMethodChannel {
|
||||
if (cancelled.get()) {
|
||||
return;
|
||||
}
|
||||
FlutterMethodChannel.this.view.sendToFlutter(name,null);
|
||||
FlutterMethodChannel.this.view.sendBinaryMessage(name, null, null);
|
||||
}
|
||||
});
|
||||
response.send(codec.encodeSuccessEnvelope(null));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG + name, "Failed to open event stream", e);
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(),null));
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
|
||||
}
|
||||
} else if (call.method.equals("cancel")) {
|
||||
cancelled.set(true);
|
||||
@ -251,7 +292,7 @@ public final class FlutterMethodChannel {
|
||||
response.send(codec.encodeSuccessEnvelope(null));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG + name, "Failed to close event stream", e);
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(),null));
|
||||
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ public final class JSONMessageCodec implements MessageCodec<Object> {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
return StringMessageCodec.INSTANCE.encodeMessage(JSONObject.wrap(message).toString());
|
||||
return StringCodec.INSTANCE.encodeMessage(JSONObject.wrap(message).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -35,7 +35,7 @@ public final class JSONMessageCodec implements MessageCodec<Object> {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final String json = StringMessageCodec.INSTANCE.decodeMessage(message);
|
||||
final String json = StringCodec.INSTANCE.decodeMessage(message);
|
||||
final JSONTokener tokener = new JSONTokener(json);
|
||||
final Object value = tokener.nextValue();
|
||||
if (tokener.more()) {
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
// Copyright 2016 The Chromium 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.common;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import io.flutter.view.FlutterView;
|
||||
|
||||
/** @deprecated Use {@link FlutterMessageChannel} and {@link JSONMessageCodec} instead. */
|
||||
@Deprecated
|
||||
public abstract class JSONMessageListener implements FlutterView.OnMessageListener {
|
||||
static final String TAG = "FlutterView";
|
||||
|
||||
@Override
|
||||
public String onMessage(FlutterView view, String message) {
|
||||
try {
|
||||
JSONObject response = onJSONMessage(view, new JSONObject(message));
|
||||
if (response == null)
|
||||
return null;
|
||||
return response.toString();
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "JSON exception", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract JSONObject onJSONMessage(FlutterView view, JSONObject message) throws JSONException;
|
||||
|
||||
public static String getStringOrNull(JSONObject object, String name) throws JSONException {
|
||||
return object.isNull(name) ? null : object.getString(name);
|
||||
}
|
||||
}
|
||||
@ -11,37 +11,77 @@ import org.json.JSONObject;
|
||||
* {@link JSONMessageCodec}.
|
||||
*/
|
||||
public final class JSONMethodCodec implements MethodCodec {
|
||||
public static final JSONMethodCodec INSTANCE = new JSONMethodCodec();
|
||||
public static final JSONMethodCodec INSTANCE = new JSONMethodCodec();
|
||||
|
||||
private JSONMethodCodec() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodCall decodeMethodCall(ByteBuffer message) {
|
||||
try {
|
||||
final Object json = JSONMessageCodec.INSTANCE.decodeMessage(message);
|
||||
if (json instanceof JSONArray) {
|
||||
final JSONArray pair = (JSONArray) json;
|
||||
if (pair.length() == 2 && pair.get(0) instanceof String) {
|
||||
return new MethodCall(pair.getString(0), pair.get(1));
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid method call: " + json);
|
||||
} catch (JSONException e) {
|
||||
throw new IllegalArgumentException("Invalid JSON", e);
|
||||
private JSONMethodCodec() {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeSuccessEnvelope(Object result) {
|
||||
return JSONMessageCodec.INSTANCE.encodeMessage(new JSONArray().put(JSONObject.wrap(result)));
|
||||
}
|
||||
@Override
|
||||
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
|
||||
try {
|
||||
final JSONObject map = new JSONObject();
|
||||
map.put("method", methodCall.method);
|
||||
map.put("args", JSONObject.wrap(methodCall.arguments));
|
||||
return JSONMessageCodec.INSTANCE.encodeMessage(map);
|
||||
} catch (JSONException e) {
|
||||
throw new IllegalArgumentException("Invalid JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails) {
|
||||
return JSONMessageCodec.INSTANCE.encodeMessage(new JSONArray()
|
||||
.put(errorCode)
|
||||
.put(errorMessage)
|
||||
.put(JSONObject.wrap(errorDetails)));
|
||||
}
|
||||
@Override
|
||||
public MethodCall decodeMethodCall(ByteBuffer message) {
|
||||
try {
|
||||
final Object json = JSONMessageCodec.INSTANCE.decodeMessage(message);
|
||||
if (json instanceof JSONObject) {
|
||||
final JSONObject map = (JSONObject) json;
|
||||
final Object method = map.get("method");
|
||||
final Object arguments = map.get("args");
|
||||
if (method instanceof String) {
|
||||
return new MethodCall((String) method, arguments);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid method call: " + json);
|
||||
} catch (JSONException e) {
|
||||
throw new IllegalArgumentException("Invalid JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeSuccessEnvelope(Object result) {
|
||||
return JSONMessageCodec.INSTANCE
|
||||
.encodeMessage(new JSONArray().put(JSONObject.wrap(result)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage,
|
||||
Object errorDetails) {
|
||||
return JSONMessageCodec.INSTANCE.encodeMessage(new JSONArray()
|
||||
.put(errorCode)
|
||||
.put(errorMessage)
|
||||
.put(JSONObject.wrap(errorDetails)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decodeEnvelope(ByteBuffer envelope) {
|
||||
try {
|
||||
final Object json = JSONMessageCodec.INSTANCE.decodeMessage(envelope);
|
||||
if (json instanceof JSONArray) {
|
||||
final JSONArray array = (JSONArray) json;
|
||||
if (array.length() == 1) {
|
||||
return array.get(0);
|
||||
}
|
||||
if (array.length() == 3) {
|
||||
final Object code = array.get(0);
|
||||
final Object message = array.get(1);
|
||||
final Object details = array.get(2);
|
||||
if (code instanceof String && (message == null || message instanceof String)) {
|
||||
throw new FlutterException((String) code, (String) message, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid method call: " + json);
|
||||
} catch (JSONException e) {
|
||||
throw new IllegalArgumentException("Invalid JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
|
||||
package io.flutter.plugin.common;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Command object representing a method call on a {@link FlutterMessageChannel}.
|
||||
*/
|
||||
|
||||
@ -16,6 +16,15 @@ import java.nio.ByteBuffer;
|
||||
* All operations throw {@link IllegalArgumentException}, if conversion fails.
|
||||
*/
|
||||
public interface MethodCodec {
|
||||
/**
|
||||
* Encodes a message call into binary.
|
||||
*
|
||||
* @param methodCall a {@link MethodCall}.
|
||||
* @return a {@link ByteBuffer} containing the encoding between position 0 and
|
||||
* the current position.
|
||||
*/
|
||||
ByteBuffer encodeMethodCall(MethodCall methodCall);
|
||||
|
||||
/**
|
||||
* Decodes a message call from binary.
|
||||
*
|
||||
@ -29,7 +38,7 @@ public interface MethodCodec {
|
||||
* Encodes a successful result into a binary envelope message.
|
||||
*
|
||||
* @param result The result value, possibly null.
|
||||
* @return a ByteBuffer containing the encoding between position 0 and
|
||||
* @return a {@link ByteBuffer} containing the encoding between position 0 and
|
||||
* the current position.
|
||||
*/
|
||||
ByteBuffer encodeSuccessEnvelope(Object result);
|
||||
@ -40,8 +49,17 @@ public interface MethodCodec {
|
||||
* @param errorCode An error code String.
|
||||
* @param errorMessage An error message String, possibly null.
|
||||
* @param errorDetails Error details, possibly null.
|
||||
* @return a ByteBuffer containing the encoding between position 0 and
|
||||
* @return a {@link ByteBuffer} containing the encoding between position 0 and
|
||||
* the current position.
|
||||
*/
|
||||
ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails);
|
||||
|
||||
/**
|
||||
* Decodes a result envelope from binary.
|
||||
*
|
||||
* @param envelope the binary encoding of a result envelope as a {@link ByteBuffer}.
|
||||
* @return the enveloped result Object.
|
||||
* @throws FlutterException if the envelope was an error envelope.
|
||||
*/
|
||||
Object decodeEnvelope(ByteBuffer envelope);
|
||||
}
|
||||
|
||||
@ -25,12 +25,22 @@ public final class StandardMethodCodec implements MethodCodec {
|
||||
private StandardMethodCodec() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
|
||||
final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
|
||||
StandardMessageCodec.writeValue(stream, methodCall.method);
|
||||
StandardMessageCodec.writeValue(stream, methodCall.arguments);
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
|
||||
buffer.put(stream.buffer(), 0, stream.size());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodCall decodeMethodCall(ByteBuffer methodCall) {
|
||||
methodCall.order(ByteOrder.nativeOrder());
|
||||
final Object method = StandardMessageCodec.readValue(methodCall);
|
||||
final Object arguments = StandardMessageCodec.readValue(methodCall);
|
||||
if (method instanceof String) {
|
||||
if (method instanceof String && !methodCall.hasRemaining()) {
|
||||
return new MethodCall((String) method, arguments);
|
||||
}
|
||||
throw new IllegalArgumentException("Method call corrupted");
|
||||
@ -58,4 +68,29 @@ public final class StandardMethodCodec implements MethodCodec {
|
||||
buffer.put(stream.buffer(), 0, stream.size());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decodeEnvelope(ByteBuffer envelope) {
|
||||
envelope.order(ByteOrder.nativeOrder());
|
||||
final byte flag = envelope.get();
|
||||
switch (flag) {
|
||||
case 0: {
|
||||
final Object result = StandardMessageCodec.readValue(envelope);
|
||||
if (!envelope.hasRemaining()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
case 1: {
|
||||
final Object code = StandardMessageCodec.readValue(envelope);
|
||||
final Object message = StandardMessageCodec.readValue(envelope);
|
||||
final Object details = StandardMessageCodec.readValue(envelope);
|
||||
if (code instanceof String
|
||||
&& (message == null || message instanceof String)
|
||||
&& !envelope.hasRemaining()) {
|
||||
throw new FlutterException((String) code, (String) message, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Envelope corrupted");
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,12 +10,12 @@ import java.nio.charset.Charset;
|
||||
/**
|
||||
* A {@link MessageCodec} using UTF-8 encoded String messages.
|
||||
*/
|
||||
public final class StringMessageCodec implements MessageCodec<String> {
|
||||
public final class StringCodec implements MessageCodec<String> {
|
||||
// This codec must match the Dart codec of the same name in package flutter/services.
|
||||
private static final Charset UTF8 = Charset.forName("UTF8");
|
||||
public static final StringMessageCodec INSTANCE = new StringMessageCodec();
|
||||
public static final StringCodec INSTANCE = new StringCodec();
|
||||
|
||||
private StringMessageCodec() {
|
||||
private StringCodec() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -6,56 +6,41 @@ package io.flutter.plugin.editing;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.Selection;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
import android.view.inputmethod.CorrectionInfo;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
import io.flutter.plugin.common.FlutterMethodChannel;
|
||||
import io.flutter.view.FlutterView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class InputConnectionAdaptor extends BaseInputConnection {
|
||||
static final String TAG = "FlutterView";
|
||||
static final String MESSAGE_NAME = "flutter/textinputclient";
|
||||
private final int mClient;
|
||||
private final TextInputPlugin mPlugin;
|
||||
private final FlutterMethodChannel mFlutterChannel;
|
||||
private final Map<String, Object> mOutgoingState;
|
||||
|
||||
private FlutterView mView;
|
||||
private int mClient;
|
||||
private TextInputPlugin mPlugin;
|
||||
private JSONObject mOutgoingState;
|
||||
|
||||
public InputConnectionAdaptor(FlutterView view, int client, TextInputPlugin plugin) {
|
||||
public InputConnectionAdaptor(FlutterView view, int client,
|
||||
TextInputPlugin plugin, FlutterMethodChannel flutterChannel) {
|
||||
super(view, true);
|
||||
mView = view;
|
||||
mClient = client;
|
||||
mPlugin = plugin;
|
||||
mOutgoingState = new JSONObject();
|
||||
mFlutterChannel = flutterChannel;
|
||||
mOutgoingState = new HashMap<>();
|
||||
}
|
||||
|
||||
private void updateEditingState() {
|
||||
try {
|
||||
final Editable content = getEditable();
|
||||
mOutgoingState.put("text", content.toString());
|
||||
mOutgoingState.put("selectionBase", Selection.getSelectionStart(content));
|
||||
mOutgoingState.put("selectionExtent", Selection.getSelectionEnd(content));
|
||||
mOutgoingState.put("composingBase", BaseInputConnection.getComposingSpanStart(content));
|
||||
mOutgoingState.put("composingExtent", BaseInputConnection.getComposingSpanEnd(content));
|
||||
|
||||
final JSONArray args = new JSONArray();
|
||||
args.put(0, mClient);
|
||||
args.put(1, mOutgoingState);
|
||||
final JSONObject message = new JSONObject();
|
||||
message.put("method", "TextInputClient.updateEditingState");
|
||||
message.put("args", args);
|
||||
mView.sendPlatformMessage(MESSAGE_NAME, message.toString(), null);
|
||||
|
||||
mPlugin.setLatestEditingState(mOutgoingState);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unexpected error serializing editing state", e);
|
||||
}
|
||||
final Editable content = getEditable();
|
||||
mOutgoingState.put("text", content.toString());
|
||||
mOutgoingState.put("selectionBase", Selection.getSelectionStart(content));
|
||||
mOutgoingState.put("selectionExtent", Selection.getSelectionEnd(content));
|
||||
mOutgoingState.put("composingBase", BaseInputConnection.getComposingSpanStart(content));
|
||||
mOutgoingState.put("composingExtent", BaseInputConnection.getComposingSpanEnd(content));
|
||||
mFlutterChannel.invokeMethod("TextInputClient.updateEditingState", Arrays
|
||||
.asList(mClient, mOutgoingState));
|
||||
mPlugin.setLatestEditingState(mOutgoingState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,7 +88,7 @@ class InputConnectionAdaptor extends BaseInputConnection {
|
||||
// 2. There is a selection. In that case, we want to delete the selection.
|
||||
// event.getNumber() is 0, and commitText("", 1) will do what we want.
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL &&
|
||||
mOutgoingState.optInt("selectionBase", -1) == mOutgoingState.optInt("selectionExtent", -1)) {
|
||||
optInt("selectionBase", -1) == optInt("selectionExtent", -1)) {
|
||||
deleteSurroundingText(1, 0);
|
||||
} else {
|
||||
String text = event.getNumber() == 0 ? "" : String.valueOf(event.getNumber());
|
||||
@ -113,21 +98,15 @@ class InputConnectionAdaptor extends BaseInputConnection {
|
||||
return result;
|
||||
}
|
||||
|
||||
private int optInt(String key, int defaultValue) {
|
||||
return mOutgoingState.containsKey(key) ? (Integer) mOutgoingState.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performEditorAction(int actionCode) {
|
||||
try {
|
||||
// TODO(abarth): Support more actions.
|
||||
final JSONArray args = new JSONArray();
|
||||
args.put(0, mClient);
|
||||
args.put(1, "TextInputAction.done");
|
||||
final JSONObject message = new JSONObject();
|
||||
message.put("method", "TextInputClient.performAction");
|
||||
message.put("args", args);
|
||||
mView.sendPlatformMessage(MESSAGE_NAME, message.toString(), null);
|
||||
return true;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unexpected error serializing editor action", e);
|
||||
return false;
|
||||
}
|
||||
// TODO(abarth): Support more actions.
|
||||
mFlutterChannel.invokeMethod("TextInputClient.performAction",
|
||||
Arrays.asList(mClient, "TextInputAction.done"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,17 +6,19 @@ package io.flutter.plugin.editing;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.View;
|
||||
import io.flutter.plugin.common.JSONMessageListener;
|
||||
|
||||
import io.flutter.plugin.common.FlutterMethodChannel;
|
||||
import io.flutter.plugin.common.FlutterMethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.FlutterMethodChannel.Response;
|
||||
import io.flutter.plugin.common.JSONMethodCodec;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.view.FlutterView;
|
||||
|
||||
import java.util.Map;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -24,37 +26,50 @@ import org.json.JSONObject;
|
||||
/**
|
||||
* Android implementation of the text input plugin.
|
||||
*/
|
||||
public class TextInputPlugin extends JSONMessageListener {
|
||||
private static final String TAG = "FlutterView";
|
||||
public class TextInputPlugin implements MethodCallHandler {
|
||||
|
||||
private final Activity mActivity;
|
||||
private final FlutterView mView;
|
||||
private final FlutterMethodChannel mFlutterChannel;
|
||||
private int mClient = 0;
|
||||
private JSONObject mConfiguration;
|
||||
private JSONObject mLatestState;
|
||||
|
||||
public TextInputPlugin(Activity activity) {
|
||||
public TextInputPlugin(Activity activity, FlutterView view) {
|
||||
mActivity = activity;
|
||||
mView = view;
|
||||
mFlutterChannel = new FlutterMethodChannel(view, "flutter/textinput",
|
||||
JSONMethodCodec.INSTANCE);
|
||||
mFlutterChannel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject onJSONMessage(FlutterView view, JSONObject message) throws JSONException {
|
||||
String method = message.getString("method");
|
||||
JSONArray args = message.getJSONArray("args");
|
||||
if (method.equals("TextInput.show")) {
|
||||
showTextInput(view);
|
||||
} else if (method.equals("TextInput.hide")) {
|
||||
hideTextInput(view);
|
||||
} else if (method.equals("TextInput.setClient")) {
|
||||
setTextInputClient(view, args.getInt(0), args.getJSONObject(1));
|
||||
} else if (method.equals("TextInput.setEditingState")) {
|
||||
setTextInputEditingState(view, args.getJSONObject(0));
|
||||
} else if (method.equals("TextInput.clearClient")) {
|
||||
clearTextInputClient();
|
||||
} else {
|
||||
// TODO(abarth): We should throw an exception here that gets
|
||||
// transmitted back to Dart.
|
||||
public void onMethodCall(MethodCall call, Response response) {
|
||||
String method = call.method;
|
||||
Object args = call.arguments;
|
||||
try {
|
||||
if (method.equals("TextInput.show")) {
|
||||
showTextInput(mView);
|
||||
response.success(null);
|
||||
} else if (method.equals("TextInput.hide")) {
|
||||
hideTextInput(mView);
|
||||
response.success(null);
|
||||
} else if (method.equals("TextInput.setClient")) {
|
||||
final JSONArray argumentList = (JSONArray) args;
|
||||
setTextInputClient(mView, argumentList.getInt(0), argumentList.getJSONObject(1));
|
||||
response.success(null);
|
||||
} else if (method.equals("TextInput.setEditingState")) {
|
||||
setTextInputEditingState(mView, (JSONObject) args);
|
||||
response.success(null);
|
||||
} else if (method.equals("TextInput.clearClient")) {
|
||||
clearTextInputClient();
|
||||
response.success(null);
|
||||
} else {
|
||||
response.error("unknown", "Unknown method: " + call.method, null);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
response.error("error", "JSON error: " + e.getMessage(), null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int inputTypeFromTextInputType(String inputType) {
|
||||
@ -67,67 +82,64 @@ public class TextInputPlugin extends JSONMessageListener {
|
||||
return InputType.TYPE_CLASS_TEXT;
|
||||
}
|
||||
|
||||
public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs) {
|
||||
public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs)
|
||||
throws JSONException {
|
||||
if (mClient == 0)
|
||||
return null;
|
||||
try {
|
||||
outAttrs.inputType = inputTypeFromTextInputType(mConfiguration.getString("inputType"));
|
||||
outAttrs.actionLabel = getStringOrNull(mConfiguration, "actionLabel");
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||
InputConnectionAdaptor connection = new InputConnectionAdaptor(view, mClient, this);
|
||||
if (mLatestState != null) {
|
||||
int selectionBase = mLatestState.getInt("selectionBase");
|
||||
int selectionExtent = mLatestState.getInt("selectionExtent");
|
||||
outAttrs.initialSelStart = selectionBase;
|
||||
outAttrs.initialSelEnd = selectionExtent;
|
||||
connection.getEditable().append(mLatestState.getString("text"));
|
||||
connection.setSelection(Math.max(selectionBase, 0),
|
||||
Math.max(selectionExtent, 0));
|
||||
connection.setComposingRegion(mLatestState.getInt("composingBase"),
|
||||
mLatestState.getInt("composingExtent"));
|
||||
} else {
|
||||
outAttrs.initialSelStart = 0;
|
||||
outAttrs.initialSelEnd = 0;
|
||||
}
|
||||
return connection;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to create input connection", e);
|
||||
outAttrs.inputType = inputTypeFromTextInputType(mConfiguration.getString("inputType"));
|
||||
outAttrs.actionLabel = mConfiguration.getString("actionLabel");
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN;
|
||||
InputConnectionAdaptor connection = new InputConnectionAdaptor(view, mClient, this,
|
||||
mFlutterChannel);
|
||||
if (mLatestState != null) {
|
||||
int selectionBase = (Integer) mLatestState.get("selectionBase");
|
||||
int selectionExtent = (Integer) mLatestState.get("selectionExtent");
|
||||
outAttrs.initialSelStart = selectionBase;
|
||||
outAttrs.initialSelEnd = selectionExtent;
|
||||
connection.getEditable().append((String) mLatestState.get("text"));
|
||||
connection.setSelection(Math.max(selectionBase, 0),
|
||||
Math.max(selectionExtent, 0));
|
||||
connection.setComposingRegion((Integer) mLatestState.get("composingBase"),
|
||||
(Integer) mLatestState.get("composingExtent"));
|
||||
} else {
|
||||
outAttrs.initialSelStart = 0;
|
||||
outAttrs.initialSelEnd = 0;
|
||||
}
|
||||
return null;
|
||||
return connection;
|
||||
}
|
||||
|
||||
private void showTextInput(FlutterView view) {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(view, 0);
|
||||
}
|
||||
|
||||
private void hideTextInput(FlutterView view) {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
|
||||
}
|
||||
|
||||
private void setTextInputClient(FlutterView view, int client, JSONObject configuration) throws JSONException {
|
||||
private void setTextInputClient(FlutterView view, int client, JSONObject configuration) {
|
||||
mLatestState = null;
|
||||
mClient = client;
|
||||
mConfiguration = configuration;
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.restartInput(view);
|
||||
}
|
||||
|
||||
private void setTextInputEditingState(FlutterView view, JSONObject state) throws JSONException {
|
||||
private void setTextInputEditingState(FlutterView view, JSONObject state) {
|
||||
mLatestState = state;
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
(InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.restartInput(view);
|
||||
}
|
||||
|
||||
void setLatestEditingState(JSONObject state) {
|
||||
mLatestState = state;
|
||||
void setLatestEditingState(Map<String, Object> state) {
|
||||
mLatestState = (JSONObject) JSONObject.wrap(state);
|
||||
}
|
||||
|
||||
|
||||
private void clearTextInputClient() {
|
||||
mClient = 0;
|
||||
}
|
||||
|
||||
@ -16,9 +16,12 @@ import android.os.Build;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.View;
|
||||
|
||||
import io.flutter.plugin.common.ActivityLifecycleListener;
|
||||
import io.flutter.plugin.common.JSONMessageListener;
|
||||
import io.flutter.view.FlutterView;
|
||||
import io.flutter.plugin.common.FlutterMethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.FlutterMethodChannel.Response;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
|
||||
import org.chromium.base.PathUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -27,7 +30,7 @@ import org.json.JSONObject;
|
||||
/**
|
||||
* Android implementation of the platform plugin.
|
||||
*/
|
||||
public class PlatformPlugin extends JSONMessageListener implements ActivityLifecycleListener {
|
||||
public class PlatformPlugin implements MethodCallHandler, ActivityLifecycleListener {
|
||||
private final Activity mActivity;
|
||||
public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
@ -39,38 +42,49 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject onJSONMessage(FlutterView view, JSONObject message) throws JSONException {
|
||||
String method = message.getString("method");
|
||||
JSONArray args = message.getJSONArray("args");
|
||||
if (method.equals("SystemSound.play")) {
|
||||
playSystemSound(args.getString(0));
|
||||
} else if (method.equals("HapticFeedback.vibrate")) {
|
||||
vibrateHapticFeedback();
|
||||
} else if (method.equals("UrlLauncher.launch")) {
|
||||
launchURL(args.getString(0));
|
||||
} else if (method.equals("SystemChrome.setPreferredOrientations")) {
|
||||
setSystemChromePreferredOrientatations(args.getJSONArray(0));
|
||||
} else if (method.equals("SystemChrome.setApplicationSwitcherDescription")) {
|
||||
setSystemChromeApplicationSwitcherDescription(args.getJSONObject(0));
|
||||
} else if (method.equals("SystemChrome.setEnabledSystemUIOverlays")) {
|
||||
setSystemChromeEnabledSystemUIOverlays(args.getJSONArray(0));
|
||||
} else if (method.equals("SystemChrome.setSystemUIOverlayStyle")) {
|
||||
setSystemChromeSystemUIOverlayStyle(args.getString(0));
|
||||
} else if (method.equals("SystemNavigator.pop")) {
|
||||
popSystemNavigator();
|
||||
} else if (method.equals("Clipboard.getData")) {
|
||||
return getClipboardData(args.getString(0));
|
||||
} else if (method.equals("Clipboard.setData")) {
|
||||
setClipboardData(args.getJSONObject(0));
|
||||
} else if (method.equals("PathProvider.getTemporaryDirectory")) {
|
||||
return getPathProviderTemporaryDirectory();
|
||||
} else if (method.equals("PathProvider.getApplicationDocumentsDirectory")) {
|
||||
return getPathProviderApplicationDocumentsDirectory();
|
||||
} else {
|
||||
// TODO(abarth): We should throw an exception here that gets
|
||||
// transmitted back to Dart.
|
||||
public void onMethodCall(MethodCall call, Response response) {
|
||||
String method = call.method;
|
||||
Object arguments = call.arguments;
|
||||
try {
|
||||
if (method.equals("SystemSound.play")) {
|
||||
playSystemSound((String) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("HapticFeedback.vibrate")) {
|
||||
vibrateHapticFeedback();
|
||||
response.success(null);
|
||||
} else if (method.equals("UrlLauncher.launch")) {
|
||||
launchURL((String) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("SystemChrome.setPreferredOrientations")) {
|
||||
setSystemChromePreferredOrientations((JSONArray) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("SystemChrome.setApplicationSwitcherDescription")) {
|
||||
setSystemChromeApplicationSwitcherDescription((JSONObject) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("SystemChrome.setEnabledSystemUIOverlays")) {
|
||||
setSystemChromeEnabledSystemUIOverlays((JSONArray) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("SystemChrome.setSystemUIOverlayStyle")) {
|
||||
setSystemChromeSystemUIOverlayStyle((String) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("SystemNavigator.pop")) {
|
||||
popSystemNavigator();
|
||||
response.success(null);
|
||||
} else if (method.equals("Clipboard.getData")) {
|
||||
response.success(getClipboardData((String) arguments));
|
||||
} else if (method.equals("Clipboard.setData")) {
|
||||
setClipboardData((JSONObject) arguments);
|
||||
response.success(null);
|
||||
} else if (method.equals("PathProvider.getTemporaryDirectory")) {
|
||||
response.success(getPathProviderTemporaryDirectory());
|
||||
} else if (method.equals("PathProvider.getApplicationDocumentsDirectory")) {
|
||||
response.success(getPathProviderApplicationDocumentsDirectory());
|
||||
} else {
|
||||
response.error("unknown", "Unknown method: " + method, null);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
response.error("error", "JSON error: " + e.getMessage(), null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void playSystemSound(String soundType) {
|
||||
@ -95,19 +109,19 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
}
|
||||
}
|
||||
|
||||
private void setSystemChromePreferredOrientatations(JSONArray orientatations) throws JSONException {
|
||||
private void setSystemChromePreferredOrientations(JSONArray orientations) throws JSONException {
|
||||
// Currently the Android implementation only supports masks with zero or one
|
||||
// selected device orientations.
|
||||
int androidOrientation;
|
||||
if (orientatations.length() == 0) {
|
||||
if (orientations.length() == 0) {
|
||||
androidOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
} else if (orientatations.getString(0).equals("DeviceOrientation.portraitUp")) {
|
||||
} else if (orientations.getString(0).equals("DeviceOrientation.portraitUp")) {
|
||||
androidOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
} else if (orientatations.getString(0).equals("DeviceOrientation.landscapeLeft")) {
|
||||
} else if (orientations.getString(0).equals("DeviceOrientation.landscapeLeft")) {
|
||||
androidOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
} else if (orientatations.getString(0).equals("DeviceOrientation.portraitDown")) {
|
||||
} else if (orientations.getString(0).equals("DeviceOrientation.portraitDown")) {
|
||||
androidOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
} else if (orientatations.getString(0).equals("DeviceOrientation.landscapeRight")) {
|
||||
} else if (orientations.getString(0).equals("DeviceOrientation.landscapeRight")) {
|
||||
androidOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
} else {
|
||||
return;
|
||||
@ -127,11 +141,11 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
}
|
||||
|
||||
mActivity.setTaskDescription(
|
||||
new android.app.ActivityManager.TaskDescription(
|
||||
description.getString("label"),
|
||||
null,
|
||||
color
|
||||
)
|
||||
new android.app.ActivityManager.TaskDescription(
|
||||
description.getString("label"),
|
||||
null,
|
||||
color
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -143,9 +157,9 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
|
||||
if (overlays.length() == 0) {
|
||||
enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
}
|
||||
if (overlays.length() == 0) {
|
||||
enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < overlays.length(); ++i) {
|
||||
String overlay = overlays.getString(i);
|
||||
@ -183,9 +197,9 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
|
||||
if ((format == null || format.equals(kTextPlainFormat)) &&
|
||||
clip.getDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("text", clip.getItemAt(0).getText().toString());
|
||||
return result;
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("text", clip.getItemAt(0).getText().toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -197,16 +211,12 @@ public class PlatformPlugin extends JSONMessageListener implements ActivityLifec
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
|
||||
private JSONObject getPathProviderTemporaryDirectory() throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("path", mActivity.getCacheDir().getPath());
|
||||
return result;
|
||||
private String getPathProviderTemporaryDirectory() {
|
||||
return mActivity.getCacheDir().getPath();
|
||||
}
|
||||
|
||||
private JSONObject getPathProviderApplicationDocumentsDirectory() throws JSONException {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("path", PathUtils.getDataDirectory(mActivity));
|
||||
return result;
|
||||
private String getPathProviderApplicationDocumentsDirectory() {
|
||||
return PathUtils.getDataDirectory(mActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -10,12 +10,10 @@ import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -10,10 +10,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import java.io.BufferedReader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -21,9 +19,6 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import org.chromium.base.JNINamespace;
|
||||
import org.chromium.base.PathUtils;
|
||||
|
||||
@ -27,38 +27,44 @@ import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import io.flutter.plugin.common.StringMessageCodec;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import io.flutter.plugin.common.ActivityLifecycleListener;
|
||||
import io.flutter.plugin.common.FlutterMessageChannel;
|
||||
import io.flutter.plugin.common.FlutterMethodChannel;
|
||||
import io.flutter.plugin.common.JSONMessageCodec;
|
||||
import io.flutter.plugin.common.JSONMethodCodec;
|
||||
import io.flutter.plugin.common.StringCodec;
|
||||
import io.flutter.plugin.editing.TextInputPlugin;
|
||||
import io.flutter.plugin.platform.PlatformPlugin;
|
||||
|
||||
import org.chromium.base.CalledByNative;
|
||||
import org.chromium.base.JNINamespace;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.plugin.common.ActivityLifecycleListener;
|
||||
import io.flutter.plugin.editing.TextInputPlugin;
|
||||
import io.flutter.plugin.platform.PlatformPlugin;
|
||||
|
||||
/**
|
||||
* An Android view containing a Flutter app.
|
||||
*/
|
||||
@JNINamespace("shell")
|
||||
public class FlutterView extends SurfaceView
|
||||
implements AccessibilityManager.AccessibilityStateChangeListener {
|
||||
implements AccessibilityManager.AccessibilityStateChangeListener {
|
||||
|
||||
private static final String TAG = "FlutterView";
|
||||
|
||||
private static final String ACTION_DISCOVER = "io.flutter.view.DISCOVER";
|
||||
|
||||
class ViewportMetrics {
|
||||
static final class ViewportMetrics {
|
||||
|
||||
float devicePixelRatio = 1.0f;
|
||||
int physicalWidth = 0;
|
||||
int physicalHeight = 0;
|
||||
@ -68,15 +74,19 @@ public class FlutterView extends SurfaceView
|
||||
int physicalPaddingLeft = 0;
|
||||
}
|
||||
|
||||
private long mNativePlatformView;
|
||||
private TextInputPlugin mTextInputPlugin;
|
||||
|
||||
private HashMap<String, OnBinaryMessageListenerAsync> mMessageListeners;
|
||||
private final TextInputPlugin mTextInputPlugin;
|
||||
private final Map<String, OnBinaryMessageListenerAsync> mMessageListeners;
|
||||
private final SurfaceHolder.Callback mSurfaceCallback;
|
||||
private final ViewportMetrics mMetrics;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private BroadcastReceiver discoveryReceiver;
|
||||
private List<ActivityLifecycleListener> mActivityLifecycleListeners;
|
||||
private final FlutterMethodChannel mFlutterLocalizationChannel;
|
||||
private final FlutterMethodChannel mFlutterNavigationChannel;
|
||||
private final FlutterMessageChannel<Object> mFlutterKeyEventChannel;
|
||||
private final FlutterMessageChannel<String> mFlutterLifecycleChannel;
|
||||
private final FlutterMessageChannel<Object> mFlutterSystemChannel;
|
||||
private final BroadcastReceiver mDiscoveryReceiver;
|
||||
private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
|
||||
private long mNativePlatformView;
|
||||
|
||||
public FlutterView(Context context) {
|
||||
this(context, null);
|
||||
@ -96,8 +106,10 @@ public class FlutterView extends SurfaceView
|
||||
int color = 0xFF000000;
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.colorBackground, typedValue, true);
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT)
|
||||
color = typedValue.data;
|
||||
if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT
|
||||
&& typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
color = typedValue.data;
|
||||
}
|
||||
// TODO(abarth): Consider letting the developer override this color.
|
||||
final int backgroundColor = color;
|
||||
|
||||
@ -122,27 +134,41 @@ public class FlutterView extends SurfaceView
|
||||
};
|
||||
getHolder().addCallback(mSurfaceCallback);
|
||||
|
||||
mAccessibilityManager = (AccessibilityManager)getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
mAccessibilityManager = (AccessibilityManager) getContext()
|
||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
|
||||
mMessageListeners = new HashMap<String, OnBinaryMessageListenerAsync>();
|
||||
mActivityLifecycleListeners = new ArrayList<ActivityLifecycleListener>();
|
||||
mMessageListeners = new HashMap<>();
|
||||
mActivityLifecycleListeners = new ArrayList<>();
|
||||
|
||||
// Configure the platform plugins and flutter channels.
|
||||
mFlutterLocalizationChannel = new FlutterMethodChannel(this, "flutter/localization",
|
||||
JSONMethodCodec.INSTANCE);
|
||||
mFlutterNavigationChannel = new FlutterMethodChannel(this, "flutter/navigation",
|
||||
JSONMethodCodec.INSTANCE);
|
||||
mFlutterKeyEventChannel = new FlutterMessageChannel<>(this, "flutter/keyevent",
|
||||
JSONMessageCodec.INSTANCE);
|
||||
mFlutterLifecycleChannel = new FlutterMessageChannel<>(this, "flutter/lifecycle",
|
||||
StringCodec.INSTANCE);
|
||||
mFlutterSystemChannel = new FlutterMessageChannel<>(this, "flutter/system",
|
||||
JSONMessageCodec.INSTANCE);
|
||||
PlatformPlugin platformPlugin = new PlatformPlugin((Activity) getContext());
|
||||
FlutterMethodChannel flutterPlatformChannel = new FlutterMethodChannel(this,
|
||||
"flutter/platform", JSONMethodCodec.INSTANCE);
|
||||
flutterPlatformChannel.setMethodCallHandler(platformPlugin);
|
||||
addActivityLifecycleListener(platformPlugin);
|
||||
mTextInputPlugin = new TextInputPlugin((Activity) getContext(), this);
|
||||
|
||||
setLocale(getResources().getConfiguration().locale);
|
||||
|
||||
// Configure the platform plugin.
|
||||
PlatformPlugin platformPlugin = new PlatformPlugin((Activity)getContext());
|
||||
addOnMessageListener("flutter/platform", platformPlugin);
|
||||
addActivityLifecycleListener(platformPlugin);
|
||||
mTextInputPlugin = new TextInputPlugin((Activity)getContext());
|
||||
addOnMessageListener("flutter/textinput", mTextInputPlugin);
|
||||
|
||||
if ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
discoveryReceiver = new DiscoveryReceiver();
|
||||
context.registerReceiver(discoveryReceiver, new IntentFilter(ACTION_DISCOVER));
|
||||
mDiscoveryReceiver = new DiscoveryReceiver();
|
||||
context.registerReceiver(mDiscoveryReceiver, new IntentFilter(ACTION_DISCOVER));
|
||||
} else {
|
||||
mDiscoveryReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeKeyEvent(KeyEvent event, JSONObject message) throws JSONException {
|
||||
private void encodeKeyEvent(KeyEvent event, Map<String, Object> message) {
|
||||
message.put("flags", event.getFlags());
|
||||
message.put("codePoint", event.getUnicodeChar());
|
||||
message.put("keyCode", event.getKeyCode());
|
||||
@ -152,35 +178,29 @@ public class FlutterView extends SurfaceView
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (!isAttached())
|
||||
if (!isAttached()) {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
|
||||
try {
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("type", "keyup");
|
||||
message.put("keymap", "android");
|
||||
encodeKeyEvent(event, message);
|
||||
sendPlatformMessage("flutter/keyevent", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to serialize key event", e);
|
||||
}
|
||||
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("type", "keyup");
|
||||
message.put("keymap", "android");
|
||||
encodeKeyEvent(event, message);
|
||||
mFlutterKeyEventChannel.send(message);
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (!isAttached())
|
||||
if (!isAttached()) {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
|
||||
try {
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("type", "keydown");
|
||||
message.put("keymap", "android");
|
||||
encodeKeyEvent(event, message);
|
||||
sendPlatformMessage("flutter/keyevent", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to serialize key event", e);
|
||||
}
|
||||
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("type", "keydown");
|
||||
message.put("keymap", "android");
|
||||
encodeKeyEvent(event, message);
|
||||
mFlutterKeyEventChannel.send(message);
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@ -189,62 +209,33 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
sendPlatformMessage("flutter/lifecycle", "AppLifecycleState.paused", null);
|
||||
mFlutterLifecycleChannel.send("AppLifecycleState.paused");
|
||||
}
|
||||
|
||||
public void onPostResume() {
|
||||
for (ActivityLifecycleListener listener : mActivityLifecycleListeners)
|
||||
for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
|
||||
listener.onPostResume();
|
||||
|
||||
sendPlatformMessage("flutter/lifecycle", "AppLifecycleState.resumed", null);
|
||||
}
|
||||
mFlutterLifecycleChannel.send("AppLifecycleState.resumed");
|
||||
}
|
||||
|
||||
public void onMemoryPressure() {
|
||||
try {
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("type", "memoryPressure");
|
||||
sendPlatformMessage("flutter/system", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to serialize system event", e);
|
||||
}
|
||||
Map<String, Object> message = new HashMap<>(1);
|
||||
message.put("type", "memoryPressure");
|
||||
mFlutterSystemChannel.send(message);
|
||||
}
|
||||
|
||||
public void pushRoute(String route) {
|
||||
try {
|
||||
final JSONArray args = new JSONArray();
|
||||
args.put(0, route);
|
||||
final JSONObject message = new JSONObject();
|
||||
message.put("method", "pushRoute");
|
||||
message.put("args", args);
|
||||
sendPlatformMessage("flutter/navigation", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unexpected JSONException pushing route", e);
|
||||
}
|
||||
mFlutterNavigationChannel.invokeMethod("pushRoute", route);
|
||||
}
|
||||
|
||||
public void popRoute() {
|
||||
try {
|
||||
final JSONObject message = new JSONObject();
|
||||
message.put("method", "popRoute");
|
||||
message.put("args", new JSONArray());
|
||||
sendPlatformMessage("flutter/navigation", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unexpected JSONException pushing route", e);
|
||||
}
|
||||
mFlutterNavigationChannel.invokeMethod("popRoute", null);
|
||||
}
|
||||
|
||||
private void setLocale(Locale locale) {
|
||||
try {
|
||||
final JSONArray args = new JSONArray();
|
||||
args.put(0, locale.getLanguage());
|
||||
args.put(1, locale.getCountry());
|
||||
final JSONObject message = new JSONObject();
|
||||
message.put("method", "setLocale");
|
||||
message.put("args", args);
|
||||
sendPlatformMessage("flutter/localization", message.toString(), null);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unexpected JSONException pushing route", e);
|
||||
}
|
||||
mFlutterLocalizationChannel.invokeMethod("setLocale",
|
||||
Arrays.asList(locale.getLanguage(), locale.getCountry()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -258,8 +249,8 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (discoveryReceiver != null) {
|
||||
getContext().unregisterReceiver(discoveryReceiver);
|
||||
if (mDiscoveryReceiver != null) {
|
||||
getContext().unregisterReceiver(mDiscoveryReceiver);
|
||||
}
|
||||
|
||||
getHolder().removeCallback(mSurfaceCallback);
|
||||
@ -269,7 +260,12 @@ public class FlutterView extends SurfaceView
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
return mTextInputPlugin.createInputConnection(this, outAttrs);
|
||||
try {
|
||||
return mTextInputPlugin.createInputConnection(this, outAttrs);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to create input connection", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Must match the PointerChange enum in pointer.dart.
|
||||
@ -327,7 +323,7 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
private void addPointerForIndex(MotionEvent event, int pointerIndex,
|
||||
ByteBuffer packet) {
|
||||
ByteBuffer packet) {
|
||||
int pointerChange = getPointerChangeForAction(event.getActionMasked());
|
||||
if (pointerChange == -1) {
|
||||
return;
|
||||
@ -348,27 +344,28 @@ public class FlutterView extends SurfaceView
|
||||
packet.putDouble(event.getY(pointerIndex)); // physical_y
|
||||
|
||||
if (pointerKind == kPointerDeviceKindMouse) {
|
||||
packet.putLong(event.getButtonState() & 0x1F); // buttons
|
||||
packet.putLong(event.getButtonState() & 0x1F); // buttons
|
||||
} else if (pointerKind == kPointerDeviceKindStylus) {
|
||||
packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons
|
||||
packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons
|
||||
} else {
|
||||
packet.putLong(0); // buttons
|
||||
packet.putLong(0); // buttons
|
||||
}
|
||||
|
||||
packet.putLong(0); // obscured
|
||||
|
||||
// TODO(eseidel): Could get the calibrated range if necessary:
|
||||
// event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE)
|
||||
packet.putDouble(event.getPressure(pointerIndex)); // presure
|
||||
packet.putDouble(event.getPressure(pointerIndex)); // pressure
|
||||
packet.putDouble(0.0); // pressure_min
|
||||
packet.putDouble(1.0); // pressure_max
|
||||
|
||||
if (pointerKind == kPointerDeviceKindStylus) {
|
||||
packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance
|
||||
packet.putDouble(0.0); // distance_max
|
||||
packet
|
||||
.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance
|
||||
packet.putDouble(0.0); // distance_max
|
||||
} else {
|
||||
packet.putDouble(0.0); // distance
|
||||
packet.putDouble(0.0); // distance_max
|
||||
packet.putDouble(0.0); // distance
|
||||
packet.putDouble(0.0); // distance_max
|
||||
}
|
||||
|
||||
packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major
|
||||
@ -377,19 +374,21 @@ public class FlutterView extends SurfaceView
|
||||
packet.putDouble(0.0); // radius_min
|
||||
packet.putDouble(0.0); // radius_max
|
||||
|
||||
packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation
|
||||
packet.putDouble(
|
||||
event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation
|
||||
|
||||
if (pointerKind == kPointerDeviceKindStylus) {
|
||||
packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt
|
||||
packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt
|
||||
} else {
|
||||
packet.putDouble(0.0); // tilt
|
||||
packet.putDouble(0.0); // tilt
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (!isAttached())
|
||||
if (!isAttached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(abarth): This version check might not be effective in some
|
||||
// versions of Android that statically compile code and will be upset
|
||||
@ -406,16 +405,17 @@ public class FlutterView extends SurfaceView
|
||||
|
||||
int pointerCount = event.getPointerCount();
|
||||
|
||||
ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField);
|
||||
ByteBuffer packet = ByteBuffer
|
||||
.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField);
|
||||
packet.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
int maskedAction = event.getActionMasked();
|
||||
// ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN
|
||||
// only apply to a single pointer, other events apply to all pointers.
|
||||
if (maskedAction == MotionEvent.ACTION_UP
|
||||
|| maskedAction == MotionEvent.ACTION_POINTER_UP
|
||||
|| maskedAction == MotionEvent.ACTION_DOWN
|
||||
|| maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
|| maskedAction == MotionEvent.ACTION_POINTER_UP
|
||||
|| maskedAction == MotionEvent.ACTION_DOWN
|
||||
|| maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
addPointerForIndex(event, event.getActionIndex(), packet);
|
||||
} else {
|
||||
// ACTION_MOVE may not actually mean all pointers have moved
|
||||
@ -433,8 +433,9 @@ public class FlutterView extends SurfaceView
|
||||
|
||||
@Override
|
||||
public boolean onHoverEvent(MotionEvent event) {
|
||||
if (!isAttached())
|
||||
if (!isAttached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = handleAccessibilityHoverEvent(event);
|
||||
if (!handled) {
|
||||
@ -499,8 +500,8 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
private void runFromSource(final String assetsDirectory,
|
||||
final String main,
|
||||
final String packages) {
|
||||
final String main,
|
||||
final String packages) {
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
preRun();
|
||||
@ -520,12 +521,13 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Thread got interrupted waiting for " +
|
||||
"RunFromSourceRunnable to finish", e);
|
||||
"RunFromSourceRunnable to finish", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the most recent frame as a bitmap.
|
||||
*
|
||||
* @return A bitmap.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
@ -533,52 +535,67 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
private static native long nativeAttach(FlutterView view);
|
||||
|
||||
private static native String nativeGetObservatoryUri();
|
||||
|
||||
private static native void nativeDetach(long nativePlatformViewAndroid);
|
||||
|
||||
private static native void nativeSurfaceCreated(long nativePlatformViewAndroid,
|
||||
Surface surface,
|
||||
int backgroundColor);
|
||||
Surface surface,
|
||||
int backgroundColor);
|
||||
|
||||
private static native void nativeSurfaceChanged(long nativePlatformViewAndroid,
|
||||
int width,
|
||||
int height);
|
||||
int width,
|
||||
int height);
|
||||
|
||||
private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid);
|
||||
|
||||
private static native void nativeRunBundleAndSnapshot(long nativePlatformViewAndroid,
|
||||
String bundlePath,
|
||||
String snapshotOverride);
|
||||
private static native void nativeRunBundleAndSource(long nativePlatformViewAndroid,
|
||||
String bundlePath,
|
||||
String main,
|
||||
String packages);
|
||||
String bundlePath,
|
||||
String snapshotOverride);
|
||||
|
||||
private static native void nativeRunBundleAndSource(long nativePlatformViewAndroid,
|
||||
String bundlePath,
|
||||
String main,
|
||||
String packages);
|
||||
|
||||
private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid,
|
||||
float devicePixelRatio,
|
||||
int physicalWidth,
|
||||
int physicalHeight,
|
||||
int physicalPaddingTop,
|
||||
int physicalPaddingRight,
|
||||
int physicalPaddingBottom,
|
||||
int physicalPaddingLeft);
|
||||
|
||||
private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid,
|
||||
float devicePixelRatio,
|
||||
int physicalWidth,
|
||||
int physicalHeight,
|
||||
int physicalPaddingTop,
|
||||
int physicalPaddingRight,
|
||||
int physicalPaddingBottom,
|
||||
int physicalPaddingLeft);
|
||||
private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid);
|
||||
|
||||
// Send a platform message to Dart.
|
||||
private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid, String channel, ByteBuffer message, int position, int responseId);
|
||||
private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, int position);
|
||||
private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action);
|
||||
private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled);
|
||||
private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid,
|
||||
String channel, ByteBuffer message, int position, int responseId);
|
||||
|
||||
private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid,
|
||||
ByteBuffer buffer, int position);
|
||||
|
||||
private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id,
|
||||
int action);
|
||||
|
||||
private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid,
|
||||
boolean enabled);
|
||||
|
||||
// Send a response to a platform message received from Dart.
|
||||
private static native void nativeInvokePlatformMessageResponseCallback(long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position);
|
||||
private static native void nativeInvokePlatformMessageResponseCallback(
|
||||
long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position);
|
||||
|
||||
private void updateViewportMetrics() {
|
||||
nativeSetViewportMetrics(mNativePlatformView,
|
||||
mMetrics.devicePixelRatio,
|
||||
mMetrics.physicalWidth,
|
||||
mMetrics.physicalHeight,
|
||||
mMetrics.physicalPaddingTop,
|
||||
mMetrics.physicalPaddingRight,
|
||||
mMetrics.physicalPaddingBottom,
|
||||
mMetrics.physicalPaddingLeft);
|
||||
mMetrics.devicePixelRatio,
|
||||
mMetrics.physicalWidth,
|
||||
mMetrics.physicalHeight,
|
||||
mMetrics.physicalPaddingTop,
|
||||
mMetrics.physicalPaddingRight,
|
||||
mMetrics.physicalPaddingBottom,
|
||||
mMetrics.physicalPaddingLeft);
|
||||
}
|
||||
|
||||
// Called by native to send us a platform message.
|
||||
@ -606,8 +623,7 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
private int mNextResponseId = 1;
|
||||
private final Map<Integer, BinaryMessageReplyCallback> mPendingResponses
|
||||
= new HashMap<Integer, BinaryMessageReplyCallback>();
|
||||
private final Map<Integer, BinaryMessageReplyCallback> mPendingResponses = new HashMap<>();
|
||||
|
||||
// Called by native to respond to a platform message that we sent.
|
||||
@CalledByNative
|
||||
@ -645,13 +661,15 @@ public class FlutterView extends SurfaceView
|
||||
super.onAttachedToWindow();
|
||||
mAccessibilityEnabled = mAccessibilityManager.isEnabled();
|
||||
mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
|
||||
if (mAccessibilityEnabled || mTouchExplorationEnabled)
|
||||
ensureAccessibilityEnabled();
|
||||
if (mAccessibilityEnabled || mTouchExplorationEnabled) {
|
||||
ensureAccessibilityEnabled();
|
||||
}
|
||||
resetWillNotDraw();
|
||||
mAccessibilityManager.addAccessibilityStateChangeListener(this);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
if (mTouchExplorationListener == null)
|
||||
if (mTouchExplorationListener == null) {
|
||||
mTouchExplorationListener = new TouchExplorationListener();
|
||||
}
|
||||
mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener);
|
||||
}
|
||||
}
|
||||
@ -660,8 +678,10 @@ public class FlutterView extends SurfaceView
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mAccessibilityManager.removeAccessibilityStateChangeListener(this);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mAccessibilityManager
|
||||
.removeTouchExplorationStateChangeListener(mTouchExplorationListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetWillNotDraw() {
|
||||
@ -683,7 +703,8 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
class TouchExplorationListener
|
||||
implements AccessibilityManager.TouchExplorationStateChangeListener {
|
||||
implements AccessibilityManager.TouchExplorationStateChangeListener {
|
||||
|
||||
@Override
|
||||
public void onTouchExplorationStateChanged(boolean enabled) {
|
||||
if (enabled) {
|
||||
@ -721,10 +742,11 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
|
||||
private boolean handleAccessibilityHoverEvent(MotionEvent event) {
|
||||
if (!mTouchExplorationEnabled)
|
||||
if (!mTouchExplorationEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER ||
|
||||
event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
|
||||
event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
|
||||
mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY());
|
||||
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
|
||||
mAccessibilityNodeProvider.handleTouchExplorationExit();
|
||||
@ -735,58 +757,17 @@ public class FlutterView extends SurfaceView
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the Flutter application. The Flutter application can
|
||||
* register a platform message handler that will receive these messages with
|
||||
* the PlatformMessages object.
|
||||
* @param channel Name of the channel that will receive this message.
|
||||
* @param message Message payload.
|
||||
* @param callback Callback that receives a reply from the application.
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void sendPlatformMessage(String channel, String message, MessageReplyCallback callback) {
|
||||
sendToFlutter(channel, message, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the Flutter application. The Flutter Dart code can register a
|
||||
* host message handler that will receive these messages.
|
||||
* @param channel Name of the channel that will receive this message.
|
||||
* @param message Message payload.
|
||||
* @param callback Callback that receives a reply from the application.
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void sendToFlutter(String channel, String message, final MessageReplyCallback callback) {
|
||||
sendBinaryMessage(channel, StringMessageCodec.INSTANCE.encodeMessage(message),
|
||||
callback == null ? null : new BinaryMessageReplyCallback() {
|
||||
@Override
|
||||
public void onReply(ByteBuffer reply) {
|
||||
callback.onReply(StringMessageCodec.INSTANCE.decodeMessage(reply));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void sendToFlutter(String channel, String message) {
|
||||
sendToFlutter(channel, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a binary message to the Flutter application. The Flutter Dart code can register a
|
||||
* platform message handler that will receive these messages.
|
||||
*
|
||||
* @param channel Name of the channel that will receive this message.
|
||||
* @param message Message payload, a {@link ByteBuffer} with the message bytes between position
|
||||
* zero and current position, or null.
|
||||
* @param callback Callback that receives a reply from the Flutter application.
|
||||
*/
|
||||
public void sendBinaryMessage(String channel, ByteBuffer message, BinaryMessageReplyCallback callback) {
|
||||
public void sendBinaryMessage(String channel, ByteBuffer message,
|
||||
BinaryMessageReplyCallback callback) {
|
||||
int responseId = 0;
|
||||
if (callback != null) {
|
||||
responseId = mNextResponseId++;
|
||||
@ -796,76 +777,23 @@ public class FlutterView extends SurfaceView
|
||||
message == null ? 0 : message.position(), responseId);
|
||||
}
|
||||
|
||||
/** Callback invoked when the app replies to a String message sent with sendToFlutter.
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
/**
|
||||
* Callback invoked when the app replies to a binary message sent with sendBinaryMessage.
|
||||
*/
|
||||
public interface MessageReplyCallback {
|
||||
void onReply(String reply);
|
||||
}
|
||||
|
||||
/** Callback invoked when the app replies to a binary message sent with sendBinaryMessage. */
|
||||
public interface BinaryMessageReplyCallback {
|
||||
|
||||
void onReply(ByteBuffer reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the Flutter application sends a message
|
||||
* to its host.
|
||||
* @param channel Name of the channel used by the application.
|
||||
* @param listener Called when messages arrive.
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void addOnMessageListener(String channel, final OnMessageListener listener) {
|
||||
addOnBinaryMessageListenerAsync(channel,
|
||||
listener == null ? null : new OnBinaryMessageListenerAsync() {
|
||||
@Override
|
||||
public void onMessage(FlutterView view, ByteBuffer message,
|
||||
BinaryMessageResponse response) {
|
||||
response.send(StringMessageCodec.INSTANCE.encodeMessage(listener.onMessage(
|
||||
view, StringMessageCodec.INSTANCE.decodeMessage(message))));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the Flutter application sends a message
|
||||
* to its host. The reply to the message can be provided asynchronously.
|
||||
* @param channel Name of the channel used by the application.
|
||||
* @param listener Called when messages arrive.
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void addOnMessageListenerAsync(String channel, final OnMessageListenerAsync listener) {
|
||||
addOnBinaryMessageListenerAsync(channel,
|
||||
listener == null ? null : new OnBinaryMessageListenerAsync() {
|
||||
@Override
|
||||
public void onMessage(FlutterView view, ByteBuffer message,
|
||||
final BinaryMessageResponse response) {
|
||||
listener.onMessage(view, StringMessageCodec.INSTANCE.decodeMessage(message),
|
||||
new MessageResponse() {
|
||||
@Override
|
||||
public void send(String reply) {
|
||||
response.send(StringMessageCodec.INSTANCE.encodeMessage(reply));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when the Flutter application sends a message
|
||||
* to its host. The reply to the message can be provided asynchronously.
|
||||
*
|
||||
* @param channel Name of the channel used by the application.
|
||||
* @param listener Called when messages arrive.
|
||||
*/
|
||||
public void addOnBinaryMessageListenerAsync(String channel, OnBinaryMessageListenerAsync listener) {
|
||||
public void addOnBinaryMessageListenerAsync(String channel,
|
||||
OnBinaryMessageListenerAsync listener) {
|
||||
if (listener == null) {
|
||||
mMessageListeners.remove(channel);
|
||||
} else {
|
||||
@ -873,39 +801,11 @@ public class FlutterView extends SurfaceView
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface OnMessageListener {
|
||||
/**
|
||||
* Called when a message is received from the Flutter app.
|
||||
* @param view The Flutter view hosting the app.
|
||||
* @param message Message payload.
|
||||
* @return the reply to the message (can be null)
|
||||
*/
|
||||
String onMessage(FlutterView view, String message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface OnMessageListenerAsync {
|
||||
/**
|
||||
* Called when a message is received from the Flutter app.
|
||||
* @param view The Flutter view hosting the app.
|
||||
* @param message Message payload.
|
||||
* @param response Used to send a reply back to the app.
|
||||
*/
|
||||
void onMessage(FlutterView view, String message, MessageResponse response);
|
||||
}
|
||||
|
||||
public interface OnBinaryMessageListenerAsync {
|
||||
|
||||
/**
|
||||
* Called when a message is received from the Flutter app.
|
||||
*
|
||||
* @param view The Flutter view hosting the app.
|
||||
* @param message Message payload.
|
||||
* @param response Used to send a reply back to the app.
|
||||
@ -913,21 +813,16 @@ public class FlutterView extends SurfaceView
|
||||
void onMessage(FlutterView view, ByteBuffer message, BinaryMessageResponse response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link io.flutter.plugin.common.FlutterMessageChannel}
|
||||
* with {@link StringMessageCodec} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface MessageResponse {
|
||||
void send(String reply);
|
||||
}
|
||||
|
||||
public interface BinaryMessageResponse {
|
||||
|
||||
void send(ByteBuffer reply);
|
||||
}
|
||||
|
||||
/** Broadcast receiver used to discover active Flutter instances. */
|
||||
/**
|
||||
* Broadcast receiver used to discover active Flutter instances.
|
||||
*/
|
||||
private class DiscoveryReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
URI observatoryUri = URI.create(nativeGetObservatoryUri());
|
||||
@ -936,7 +831,8 @@ public class FlutterView extends SurfaceView
|
||||
discover.put("id", getContext().getPackageName());
|
||||
discover.put("observatoryPort", observatoryUri.getPort());
|
||||
Log.i(TAG, "DISCOVER: " + discover);
|
||||
} catch (JSONException e) {}
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A class to clean up orphaned resource directories after unclean shutdowns.
|
||||
|
||||
@ -17,14 +17,11 @@ shared_library("flutter_framework_dylib") {
|
||||
sources = [
|
||||
"framework/Headers/Flutter.h",
|
||||
"framework/Headers/FlutterAppDelegate.h",
|
||||
"framework/Headers/FlutterAsyncMessageListener.h",
|
||||
"framework/Headers/FlutterChannels.h",
|
||||
"framework/Headers/FlutterCodecs.h",
|
||||
"framework/Headers/FlutterBinaryMessenger.h",
|
||||
"framework/Headers/FlutterDartProject.h",
|
||||
"framework/Headers/FlutterJSONMessageListener.h",
|
||||
"framework/Headers/FlutterMacros.h",
|
||||
"framework/Headers/FlutterMessageListener.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
"framework/Source/FlutterAppDelegate.mm",
|
||||
"framework/Source/FlutterChannels.mm",
|
||||
@ -33,7 +30,6 @@ shared_library("flutter_framework_dylib") {
|
||||
"framework/Source/FlutterDartProject_Internal.h",
|
||||
"framework/Source/FlutterDartSource.h",
|
||||
"framework/Source/FlutterDartSource.mm",
|
||||
"framework/Source/FlutterJSONMessageListener.mm",
|
||||
"framework/Source/FlutterPlatformPlugin.h",
|
||||
"framework/Source/FlutterPlatformPlugin.mm",
|
||||
"framework/Source/FlutterStandardCodec_Internal.h",
|
||||
@ -158,14 +154,11 @@ copy("framework_headers") {
|
||||
sources = [
|
||||
"framework/Headers/Flutter.h",
|
||||
"framework/Headers/FlutterAppDelegate.h",
|
||||
"framework/Headers/FlutterAsyncMessageListener.h",
|
||||
"framework/Headers/FlutterBinaryMessenger.h",
|
||||
"framework/Headers/FlutterChannels.h",
|
||||
"framework/Headers/FlutterCodecs.h",
|
||||
"framework/Headers/FlutterDartProject.h",
|
||||
"framework/Headers/FlutterJSONMessageListener.h",
|
||||
"framework/Headers/FlutterMacros.h",
|
||||
"framework/Headers/FlutterMessageListener.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
]
|
||||
outputs = [
|
||||
|
||||
@ -6,14 +6,11 @@
|
||||
#define FLUTTER_FLUTTER_H_
|
||||
|
||||
#include "FlutterAppDelegate.h"
|
||||
#include "FlutterAsyncMessageListener.h"
|
||||
#include "FlutterBinaryMessenger.h"
|
||||
#include "FlutterChannels.h"
|
||||
#include "FlutterCodecs.h"
|
||||
#include "FlutterDartProject.h"
|
||||
#include "FlutterJSONMessageListener.h"
|
||||
#include "FlutterMacros.h"
|
||||
#include "FlutterMessageListener.h"
|
||||
#include "FlutterViewController.h"
|
||||
|
||||
#endif // FLUTTER_FLUTTER_H_
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FLUTTERASYNCMESSAGELISTENER_H_
|
||||
#define FLUTTER_FLUTTERASYNCMESSAGELISTENER_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "FlutterMacros.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@protocol FlutterAsyncMessageListener<NSObject>
|
||||
|
||||
- (void)didReceiveString:(NSString*)message
|
||||
callback:(void(^)(NSString*))sendResponse;
|
||||
|
||||
@property(readonly, strong, nonatomic) NSString* messageName;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERASYNCMESSAGELISTENER_H_
|
||||
@ -17,6 +17,10 @@ FLUTTER_EXPORT
|
||||
+ (instancetype)messageChannelNamed:(NSString*)name
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
|
||||
codec:(NSObject<FlutterMessageCodec>*)codec;
|
||||
- (instancetype)initWithName:(NSString*)name
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
|
||||
codec:(NSObject<FlutterMessageCodec>*)codec;
|
||||
- (void)sendMessage:(id)message;
|
||||
- (void)sendMessage:(id)message replyHandler:(FlutterReplyHandler)handler;
|
||||
- (void)setMessageHandler:(FlutterMessageHandler)handler;
|
||||
@end
|
||||
@ -37,6 +41,13 @@ FLUTTER_EXPORT
|
||||
+ (instancetype)methodChannelNamed:(NSString*)name
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
|
||||
codec:(NSObject<FlutterMethodCodec>*)codec;
|
||||
- (instancetype)initWithName:(NSString*)name
|
||||
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
|
||||
codec:(NSObject<FlutterMethodCodec>*)codec;
|
||||
- (void)invokeMethod:(NSString*)method arguments:(id)arguments;
|
||||
- (void)invokeMethod:(NSString*)method
|
||||
arguments:(id)arguments
|
||||
resultReceiver:(FlutterResultReceiver)resultReceiver;
|
||||
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler;
|
||||
- (void)setStreamHandler:(FlutterStreamHandler)handler;
|
||||
@end
|
||||
|
||||
@ -35,8 +35,8 @@ FLUTTER_EXPORT
|
||||
@interface FlutterMethodCall : NSObject
|
||||
+ (instancetype)methodCallWithMethodName:(NSString*)method
|
||||
arguments:(id)arguments;
|
||||
@property(readonly) NSString* method;
|
||||
@property(readonly) id arguments;
|
||||
@property(readonly, nonatomic) NSString* method;
|
||||
@property(readonly, nonatomic) id arguments;
|
||||
@end
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@ -44,9 +44,9 @@ FLUTTER_EXPORT
|
||||
+ (instancetype)errorWithCode:(NSString*)code
|
||||
message:(NSString*)message
|
||||
details:(id)details;
|
||||
@property(readonly) NSString* code;
|
||||
@property(readonly) NSString* message;
|
||||
@property(readonly) id details;
|
||||
@property(readonly, nonatomic) NSString* code;
|
||||
@property(readonly, nonatomic) NSString* message;
|
||||
@property(readonly, nonatomic) id details;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, FlutterStandardDataType) {
|
||||
@ -62,24 +62,26 @@ FLUTTER_EXPORT
|
||||
+ (instancetype)typedDataWithInt32:(NSData*)data;
|
||||
+ (instancetype)typedDataWithInt64:(NSData*)data;
|
||||
+ (instancetype)typedDataWithFloat64:(NSData*)data;
|
||||
@property(readonly) NSData* data;
|
||||
@property(readonly) FlutterStandardDataType type;
|
||||
@property(readonly) UInt32 elementCount;
|
||||
@property(readonly) UInt8 elementSize;
|
||||
@property(readonly, nonatomic) NSData* data;
|
||||
@property(readonly, nonatomic) FlutterStandardDataType type;
|
||||
@property(readonly, nonatomic) UInt32 elementCount;
|
||||
@property(readonly, nonatomic) UInt8 elementSize;
|
||||
@end
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterStandardBigInteger : NSObject
|
||||
+ (instancetype)bigIntegerWithHex:(NSString*)hex;
|
||||
@property(readonly) NSString* hex;
|
||||
@property(readonly, nonatomic) NSString* hex;
|
||||
@end
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@protocol FlutterMethodCodec
|
||||
+ (instancetype)sharedInstance;
|
||||
- (FlutterMethodCall*)decodeMethodCall:(NSData*)message;
|
||||
- (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall;
|
||||
- (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall;
|
||||
- (NSData*)encodeSuccessEnvelope:(id)result;
|
||||
- (NSData*)encodeErrorEnvelope:(FlutterError*)error;
|
||||
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error;
|
||||
@end
|
||||
|
||||
FLUTTER_EXPORT
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
#define FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
|
||||
#include "FlutterMessageListener.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterJSONMessageListener : NSObject<FlutterMessageListener>
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
@ -1,21 +0,0 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FLUTTERMESSAGELISTENER_H_
|
||||
#define FLUTTER_FLUTTERMESSAGELISTENER_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "FlutterMacros.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@protocol FlutterMessageListener<NSObject>
|
||||
|
||||
- (NSString*)didReceiveString:(NSString*)message;
|
||||
|
||||
@property(readonly, strong, nonatomic) NSString* messageName;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERMESSAGELISTENER_H_
|
||||
@ -8,11 +8,9 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include "FlutterAsyncMessageListener.h"
|
||||
#include "FlutterBinaryMessenger.h"
|
||||
#include "FlutterDartProject.h"
|
||||
#include "FlutterMacros.h"
|
||||
#include "FlutterMessageListener.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterViewController : UIViewController<FlutterBinaryMessenger>
|
||||
@ -22,22 +20,6 @@ FLUTTER_EXPORT
|
||||
bundle:(NSBundle*)nibBundleOrNil
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)sendString:(NSString*)message withMessageName:(NSString*)messageName;
|
||||
|
||||
- (void)sendString:(NSString*)message
|
||||
withMessageName:(NSString*)messageName
|
||||
callback:(void (^)(NSString*))callback;
|
||||
|
||||
- (void)addMessageListener:(NSObject<FlutterMessageListener>*)listener;
|
||||
|
||||
- (void)removeMessageListener:(NSObject<FlutterMessageListener>*)listener;
|
||||
|
||||
- (void)addAsyncMessageListener:
|
||||
(NSObject<FlutterAsyncMessageListener>*)listener;
|
||||
|
||||
- (void)removeAsyncMessageListener:
|
||||
(NSObject<FlutterAsyncMessageListener>*)listener;
|
||||
|
||||
- (void)handleStatusBarTouches:(UIEvent*)event;
|
||||
@end
|
||||
|
||||
|
||||
@ -37,6 +37,11 @@
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)sendMessage:(id)message {
|
||||
[_messenger sendBinaryMessage:[_codec encode:message]
|
||||
channelName:_name];
|
||||
}
|
||||
|
||||
- (void)sendMessage:(id)message replyHandler:(FlutterReplyHandler)handler {
|
||||
[_messenger sendBinaryMessage:[_codec encode:message]
|
||||
channelName:_name
|
||||
@ -147,6 +152,25 @@
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)invokeMethod:(NSString*)method arguments:(id)arguments {
|
||||
[_messenger sendBinaryMessage:[_codec encodeMethodCall:[FlutterMethodCall methodCallWithMethodName:method arguments:arguments]]
|
||||
channelName:_name];
|
||||
}
|
||||
|
||||
- (void)invokeMethod:(NSString*)method
|
||||
arguments:(id)arguments
|
||||
resultReceiver:(FlutterResultReceiver)resultReceiver {
|
||||
[_messenger sendBinaryMessage:[_codec encodeMethodCall:[FlutterMethodCall methodCallWithMethodName:method arguments:arguments]]
|
||||
channelName:_name
|
||||
binaryReplyHandler:^(NSData* reply) {
|
||||
if (resultReceiver) {
|
||||
FlutterError* flutterError = nil;
|
||||
id result = [_codec decodeEnvelope:reply error:&flutterError];
|
||||
resultReceiver(result, flutterError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
|
||||
[_messenger
|
||||
setBinaryMessageHandlerOnChannel:_name
|
||||
|
||||
@ -78,20 +78,43 @@
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
- (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
|
||||
return [[FlutterJSONMessageCodec sharedInstance] encode:@{
|
||||
@"method": call.method,
|
||||
@"args": (call.arguments == nil ? [NSNull null] : call.arguments),
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSData*)encodeSuccessEnvelope:(id)result {
|
||||
return [[FlutterJSONMessageCodec sharedInstance] encode:@[ result ]];
|
||||
return [[FlutterJSONMessageCodec sharedInstance] encode:@[
|
||||
result == nil ? [NSNull null] : result
|
||||
]];
|
||||
}
|
||||
|
||||
- (NSData*)encodeErrorEnvelope:(FlutterError*)error {
|
||||
return [[FlutterJSONMessageCodec sharedInstance]
|
||||
encode:@[ error.code, error.message, error.details ]];
|
||||
return [[FlutterJSONMessageCodec sharedInstance] encode:@[
|
||||
error.code,
|
||||
error.message == nil ? [NSNull null] : error.message,
|
||||
error.details == nil ? [NSNull null] : error.details,
|
||||
]];
|
||||
}
|
||||
|
||||
- (FlutterMethodCall*)decodeMethodCall:(NSData*)message {
|
||||
NSArray* call = [[FlutterJSONMessageCodec sharedInstance] decode:message];
|
||||
NSAssert(call.count == 2, @"Invalid JSON method call");
|
||||
NSAssert([call[0] isKindOfClass:[NSString class]],
|
||||
@"Invalid JSON method call");
|
||||
return [FlutterMethodCall methodCallWithMethodName:call[0] arguments:call[1]];
|
||||
NSDictionary* dictionary = [[FlutterJSONMessageCodec sharedInstance] decode:message];
|
||||
id method = dictionary[@"method"];
|
||||
id arguments = dictionary[@"args"];
|
||||
NSAssert([method isKindOfClass:[NSString class]], @"Invalid JSON method call");
|
||||
return [FlutterMethodCall methodCallWithMethodName:method arguments:arguments];
|
||||
}
|
||||
|
||||
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error {
|
||||
NSArray* array = [[FlutterJSONMessageCodec sharedInstance] decode:envelope];
|
||||
if (array.count == 1)
|
||||
return array[0];
|
||||
NSAssert(array.count == 3, @"Invalid JSON envelope");
|
||||
NSAssert([array[0] isKindOfClass:[NSString class]], @"Invalid JSON envelope");
|
||||
NSAssert(array[1] == nil || [array[1] isKindOfClass:[NSString class]], @"Invalid JSON envelope");
|
||||
*error = [FlutterError errorWithCode:array[0] message:array[1] details:array[2]];
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
|
||||
|
||||
@implementation FlutterJSONMessageListener
|
||||
|
||||
- (NSString*)didReceiveString:(NSString*)message {
|
||||
if (!message)
|
||||
return nil;
|
||||
NSError *error = nil;
|
||||
NSData* data = [message dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error)
|
||||
return nil;
|
||||
NSDictionary* response = [self didReceiveJSON:jsonObject];
|
||||
if (!response)
|
||||
return nil;
|
||||
NSData* responseData = [NSJSONSerialization dataWithJSONObject:response options:0 error:nil];
|
||||
if (!responseData)
|
||||
return nil;
|
||||
return [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
|
||||
}
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)messageName {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -5,9 +5,11 @@
|
||||
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMPLUGIN_H_
|
||||
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMPLUGIN_H_
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h"
|
||||
|
||||
@interface FlutterPlatformPlugin : FlutterJSONMessageListener
|
||||
@interface FlutterPlatformPlugin : NSObject
|
||||
|
||||
-(void)handleMethodCall:(FlutterMethodCall*)call resultReceiver:(FlutterResultReceiver)resultReceiver;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -41,42 +41,45 @@ using namespace shell;
|
||||
|
||||
@implementation FlutterPlatformPlugin
|
||||
|
||||
- (NSString *)messageName {
|
||||
return @"flutter/platform";
|
||||
}
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
|
||||
NSString* method = message[@"method"];
|
||||
NSArray* args = message[@"args"];
|
||||
- (void)handleMethodCall:(FlutterMethodCall*)call resultReceiver:(FlutterResultReceiver)resultReceiver {
|
||||
NSString* method = call.method;
|
||||
id args = call.arguments;
|
||||
if ([method isEqualToString:@"SystemSound.play"]) {
|
||||
[self playSystemSound:args.firstObject];
|
||||
[self playSystemSound:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"HapticFeedback.vibrate"]) {
|
||||
[self vibrateHapticFeedback];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"UrlLauncher.launch"]) {
|
||||
[self launchURL:args.firstObject];
|
||||
[self launchURL:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"SystemChrome.setPreferredOrientations"]) {
|
||||
[self setSystemChromePreferredOrientatations:args.firstObject];
|
||||
[self setSystemChromePreferredOrientations:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"SystemChrome.setApplicationSwitcherDescription"]) {
|
||||
[self setSystemChromeApplicationSwitcherDescription:args.firstObject];
|
||||
[self setSystemChromeApplicationSwitcherDescription:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIOverlays"]) {
|
||||
[self setSystemChromeEnabledSystemUIOverlays:args.firstObject];
|
||||
[self setSystemChromeEnabledSystemUIOverlays:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"SystemChrome.setSystemUIOverlayStyle"]) {
|
||||
[self setSystemChromeSystemUIOverlayStyle:args.firstObject];
|
||||
[self setSystemChromeSystemUIOverlayStyle:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"SystemNavigator.pop"]) {
|
||||
[self popSystemNavigator];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"Clipboard.getData"]) {
|
||||
return [self getClipboardData:args.firstObject];
|
||||
resultReceiver([self getClipboardData:args], nil);
|
||||
} else if ([method isEqualToString:@"Clipboard.setData"]) {
|
||||
[self setClipboardData:args.firstObject];
|
||||
[self setClipboardData:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"PathProvider.getTemporaryDirectory"]) {
|
||||
return [self getPathProviderTemporaryDirectory];
|
||||
resultReceiver([self getPathProviderTemporaryDirectory], nil);
|
||||
} else if ([method isEqualToString:@"PathProvider.getApplicationDocumentsDirectory"]) {
|
||||
return [self getPathProviderApplicationDocumentsDirectory];
|
||||
resultReceiver([self getPathProviderApplicationDocumentsDirectory], nil);
|
||||
} else {
|
||||
// TODO(abarth): We should signal an error here that gets reported back to
|
||||
// Dart.
|
||||
resultReceiver(nil, [FlutterError errorWithCode:@"UNKNOWN" message:@"Unknown method" details: nil]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)playSystemSound:(NSString*)soundType {
|
||||
@ -99,7 +102,7 @@ using namespace shell;
|
||||
return @{ @"succes": @(success) };
|
||||
}
|
||||
|
||||
- (void)setSystemChromePreferredOrientatations:(NSArray*)orientations {
|
||||
- (void)setSystemChromePreferredOrientations:(NSArray*)orientations {
|
||||
UIInterfaceOrientationMask mask = 0;
|
||||
|
||||
if (orientations.count == 0) {
|
||||
|
||||
@ -42,6 +42,14 @@
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
- (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
|
||||
NSMutableData* data = [NSMutableData dataWithCapacity:32];
|
||||
FlutterStandardWriter* writer = [FlutterStandardWriter writerWithData:data];
|
||||
[writer writeValue:call.method];
|
||||
[writer writeValue:call.arguments];
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSData*)encodeSuccessEnvelope:(id)result {
|
||||
NSMutableData* data = [NSMutableData dataWithCapacity:32];
|
||||
FlutterStandardWriter* writer = [FlutterStandardWriter writerWithData:data];
|
||||
@ -70,6 +78,33 @@
|
||||
@"Corrupted standard method call");
|
||||
return [FlutterMethodCall methodCallWithMethodName:value1 arguments:value2];
|
||||
}
|
||||
|
||||
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error {
|
||||
FlutterStandardReader* reader =
|
||||
[FlutterStandardReader readerWithData:envelope];
|
||||
UInt8 flag = [reader readByte];
|
||||
NSAssert(flag <= 1, @"Corrupted standard envelope");
|
||||
id result;
|
||||
switch (flag) {
|
||||
case 0: {
|
||||
result = [reader readValue];
|
||||
NSAssert(![reader hasMore], @"Corrupted standard envelope");
|
||||
}
|
||||
break;
|
||||
case 1: {
|
||||
id code = [reader readValue];
|
||||
id message = [reader readValue];
|
||||
id details = [reader readValue];
|
||||
NSAssert(![reader hasMore], @"Corrupted standard envelope");
|
||||
NSAssert([code isKindOfClass:[NSString class]], @"Invalid standard envelope");
|
||||
NSAssert(message == nil || [message isKindOfClass:[NSString class]], @"Invalid standard envelope");
|
||||
*error = [FlutterError errorWithCode:code message:message details:details];
|
||||
result = nil;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@end
|
||||
|
||||
using namespace shell;
|
||||
|
||||
@ -56,6 +56,7 @@ UInt8 elementSizeForFlutterStandardDataType(FlutterStandardDataType type) {
|
||||
@interface FlutterStandardReader : NSObject
|
||||
+ (instancetype)readerWithData:(NSData*)data;
|
||||
- (BOOL)hasMore;
|
||||
- (UInt8)readByte;
|
||||
- (id)readValue;
|
||||
@end
|
||||
|
||||
|
||||
@ -5,12 +5,13 @@
|
||||
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_
|
||||
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
|
||||
|
||||
@interface FlutterTextInputPlugin : FlutterJSONMessageListener
|
||||
@interface FlutterTextInputPlugin : NSObject
|
||||
|
||||
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
|
||||
-(void)handleMethodCall:(FlutterMethodCall*)call resultReceiver:(FlutterResultReceiver)resultReceiver;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -154,30 +154,27 @@ static UIKeyboardType ToUIKeyboardType(NSString* inputType) {
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString *)messageName {
|
||||
return @"flutter/textinput";
|
||||
}
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
|
||||
NSString* method = message[@"method"];
|
||||
NSArray* args = message[@"args"];
|
||||
if (!args)
|
||||
return nil;
|
||||
- (void)handleMethodCall:(FlutterMethodCall*)call resultReceiver:(FlutterResultReceiver)resultReceiver {
|
||||
NSString* method = call.method;
|
||||
id args = call.arguments;
|
||||
if ([method isEqualToString:@"TextInput.show"]) {
|
||||
[self showTextInput];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"TextInput.hide"]) {
|
||||
[self hideTextInput];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"TextInput.setClient"]) {
|
||||
[self setTextInputClient:[args[0] intValue] withConfiguration:args[1]];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"TextInput.setEditingState"]) {
|
||||
[self setTextInputEditingState:args.firstObject];
|
||||
[self setTextInputEditingState:args];
|
||||
resultReceiver(nil, nil);
|
||||
} else if ([method isEqualToString:@"TextInput.clearClient"]) {
|
||||
[self clearTextInputClient];
|
||||
resultReceiver(nil, nil);
|
||||
} else {
|
||||
// TODO(abarth): We should signal an error here that gets reported back to
|
||||
// Dart.
|
||||
resultReceiver(nil, [FlutterError errorWithCode:@"UNKNOWN" message:@"Unknown method" details: nil]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)showTextInput {
|
||||
|
||||
@ -68,7 +68,12 @@ void FlutterInit(int argc, const char* argv[]) {
|
||||
std::unique_ptr<shell::PlatformViewIOS> _platformView;
|
||||
base::scoped_nsprotocol<FlutterPlatformPlugin*> _platformPlugin;
|
||||
base::scoped_nsprotocol<FlutterTextInputPlugin*> _textInputPlugin;
|
||||
|
||||
base::scoped_nsprotocol<FlutterMethodChannel*> _localizationChannel;
|
||||
base::scoped_nsprotocol<FlutterMethodChannel*> _navigationChannel;
|
||||
base::scoped_nsprotocol<FlutterMethodChannel*> _platformChannel;
|
||||
base::scoped_nsprotocol<FlutterMethodChannel*> _textInputChannel;
|
||||
base::scoped_nsprotocol<FlutterMessageChannel*> _lifecycleChannel;
|
||||
base::scoped_nsprotocol<FlutterMessageChannel*> _systemChannel;
|
||||
BOOL _initialized;
|
||||
}
|
||||
|
||||
@ -118,15 +123,49 @@ void FlutterInit(int argc, const char* argv[]) {
|
||||
_orientationPreferences = UIInterfaceOrientationMaskAll;
|
||||
_statusBarStyle = UIStatusBarStyleDefault;
|
||||
_platformView = std::make_unique<shell::PlatformViewIOS>(
|
||||
reinterpret_cast<CAEAGLLayer*>(self.view.layer));
|
||||
reinterpret_cast<CAEAGLLayer*>(self.view.layer));
|
||||
_platformView->SetupResourceContextOnIOThread();
|
||||
|
||||
_localizationChannel.reset([[FlutterMethodChannel alloc]
|
||||
initWithName:@"flutter/localization"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMethodCodec sharedInstance]]);
|
||||
|
||||
_navigationChannel.reset([[FlutterMethodChannel alloc]
|
||||
initWithName:@"flutter/navigation"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMethodCodec sharedInstance]]);
|
||||
|
||||
_platformChannel.reset([[FlutterMethodChannel alloc]
|
||||
initWithName:@"flutter/platform"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMethodCodec sharedInstance]]);
|
||||
|
||||
_textInputChannel.reset([[FlutterMethodChannel alloc]
|
||||
initWithName:@"flutter/textinput"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMethodCodec sharedInstance]]);
|
||||
|
||||
_lifecycleChannel.reset([[FlutterMessageChannel alloc]
|
||||
initWithName:@"flutter/lifecycle"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterStringCodec sharedInstance]]);
|
||||
|
||||
_systemChannel.reset([[FlutterMessageChannel alloc]
|
||||
initWithName:@"flutter/system"
|
||||
binaryMessenger:self
|
||||
codec:[FlutterJSONMessageCodec sharedInstance]]);
|
||||
|
||||
_platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
|
||||
[self addMessageListener:_platformPlugin.get()];
|
||||
[_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResultReceiver resultReceiver) {
|
||||
[_platformPlugin.get() handleMethodCall:call resultReceiver:resultReceiver];
|
||||
}];
|
||||
|
||||
_textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]);
|
||||
_textInputPlugin.get().textInputDelegate = self;
|
||||
[self addMessageListener:_textInputPlugin.get()];
|
||||
[_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResultReceiver resultReceiver) {
|
||||
[_textInputPlugin.get() handleMethodCall:call resultReceiver:resultReceiver];
|
||||
}];
|
||||
|
||||
[self setupNotificationCenterObservers];
|
||||
|
||||
@ -227,13 +266,11 @@ void FlutterInit(int argc, const char* argv[]) {
|
||||
#pragma mark - Application lifecycle notifications
|
||||
|
||||
- (void)applicationBecameActive:(NSNotification*)notification {
|
||||
[self sendString:@"AppLifecycleState.resumed"
|
||||
withMessageName:@"flutter/lifecycle"];
|
||||
[_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(NSNotification*)notification {
|
||||
[self sendString:@"AppLifecycleState.paused"
|
||||
withMessageName:@"flutter/lifecycle"];
|
||||
[_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"];
|
||||
}
|
||||
|
||||
#pragma mark - Touch event handling
|
||||
@ -392,11 +429,7 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
|
||||
#pragma mark - Text input delegate
|
||||
|
||||
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
|
||||
NSDictionary* message = @{
|
||||
@"method" : @"TextInputClient.updateEditingState",
|
||||
@"args" : @[ @(client), state ],
|
||||
};
|
||||
[self sendJSON:message withMessageName:@"flutter/textinputclient"];
|
||||
[_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" arguments:@[ @(client), state ]];
|
||||
}
|
||||
|
||||
#pragma mark - Orientation updates
|
||||
@ -446,8 +479,7 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
|
||||
#pragma mark - Memory Notifications
|
||||
|
||||
- (void)onMemoryWarning:(NSNotification*)notification {
|
||||
NSDictionary* message = @{ @"type" : @"memoryPressure" };
|
||||
[self sendJSON:message withMessageName:@"flutter/system"];
|
||||
[_systemChannel.get() sendMessage:@{ @"type" : @"memoryPressure" }];
|
||||
}
|
||||
|
||||
#pragma mark - Locale updates
|
||||
@ -456,10 +488,7 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
|
||||
NSLocale* currentLocale = [NSLocale currentLocale];
|
||||
NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
|
||||
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
|
||||
NSDictionary* message =
|
||||
@{ @"method" : @"setLocale",
|
||||
@"args" : @[ languageCode, countryCode ] };
|
||||
[self sendJSON:message withMessageName:@"flutter/localization"];
|
||||
[_localizationChannel.get() invokeMethod:@"setLocale" arguments: @[ languageCode, countryCode ]];
|
||||
}
|
||||
|
||||
#pragma mark - Surface creation and teardown updates
|
||||
@ -549,79 +578,6 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0;
|
||||
|
||||
#pragma mark - Application Messages
|
||||
|
||||
- (void)sendString:(NSString*)message withMessageName:(NSString*)channel {
|
||||
NSAssert(message, @"The message must not be null");
|
||||
NSAssert(channel, @"The channel must not be null");
|
||||
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
|
||||
[self sendBinaryMessage:[codec encode:message] channelName:channel];
|
||||
}
|
||||
|
||||
- (void)sendString:(NSString*)message
|
||||
withMessageName:(NSString*)channel
|
||||
callback:(void (^)(NSString*))callback {
|
||||
NSAssert(message, @"The message must not be null");
|
||||
NSAssert(channel, @"The channel must not be null");
|
||||
NSAssert(callback, @"The callback must not be null");
|
||||
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
|
||||
[self sendBinaryMessage:[codec encode:message]
|
||||
channelName:channel
|
||||
binaryReplyHandler:^(NSData* data) {
|
||||
callback([codec decode:data]);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendJSON:(NSDictionary*)message withMessageName:(NSString*)channel {
|
||||
NSData* data = [[FlutterJSONMessageCodec sharedInstance] encode:message];
|
||||
[self sendBinaryMessage:data channelName:channel];
|
||||
}
|
||||
|
||||
- (void)addMessageListener:(NSObject<FlutterMessageListener>*)listener {
|
||||
NSAssert(listener, @"The listener must not be null");
|
||||
NSString* messageName = listener.messageName;
|
||||
NSAssert(messageName, @"The messageName must not be null");
|
||||
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
|
||||
[self
|
||||
setBinaryMessageHandlerOnChannel:messageName
|
||||
binaryMessageHandler:^(
|
||||
NSData* message, FlutterBinaryReplyHandler replyHandler) {
|
||||
NSString* reply =
|
||||
[listener didReceiveString:[codec decode:message]];
|
||||
replyHandler([codec encode:reply]);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeMessageListener:(NSObject<FlutterMessageListener>*)listener {
|
||||
NSAssert(listener, @"The listener must not be null");
|
||||
NSString* messageName = listener.messageName;
|
||||
NSAssert(messageName, @"The messageName must not be null");
|
||||
[self setBinaryMessageHandlerOnChannel:messageName binaryMessageHandler:nil];
|
||||
}
|
||||
|
||||
- (void)addAsyncMessageListener:
|
||||
(NSObject<FlutterAsyncMessageListener>*)listener {
|
||||
NSAssert(listener, @"The listener must not be null");
|
||||
NSString* messageName = listener.messageName;
|
||||
NSAssert(messageName, @"The messageName must not be null");
|
||||
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
|
||||
[self
|
||||
setBinaryMessageHandlerOnChannel:messageName
|
||||
binaryMessageHandler:^(
|
||||
NSData* message, FlutterBinaryReplyHandler replyHandler) {
|
||||
[listener didReceiveString:[codec decode:message]
|
||||
callback:^(NSString* reply) {
|
||||
replyHandler([codec encode:reply]);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeAsyncMessageListener:
|
||||
(NSObject<FlutterAsyncMessageListener>*)listener {
|
||||
NSAssert(listener, @"The listener must not be null");
|
||||
NSString* messageName = listener.messageName;
|
||||
NSAssert(messageName, @"The messageName must not be null");
|
||||
[self setBinaryMessageHandlerOnChannel:messageName binaryMessageHandler:nil];
|
||||
}
|
||||
|
||||
- (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channel {
|
||||
NSAssert(message, @"The message must not be null");
|
||||
NSAssert(channel, @"The channel must not be null");
|
||||
|
||||
@ -1781,7 +1781,6 @@ FILE: ../../../flutter/shell/platform/android/android_surface_gl.h
|
||||
FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.cc
|
||||
FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.h
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMessageListener.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java
|
||||
@ -1794,17 +1793,13 @@ FILE: ../../../flutter/shell/platform/darwin/common/process_info_mac.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/Flutter.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAsyncMessageListener.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterMessageListener.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterJSONMessageListener.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h
|
||||
@ -1915,7 +1910,8 @@ FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc
|
||||
FILE: ../../../flutter/fml/platform/darwin/cf_utils.h
|
||||
FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc
|
||||
FILE: ../../../flutter/shell/gpu/gpu_surface_software.h
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryMessageCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterException.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMessageChannel.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMethodChannel.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java
|
||||
@ -1924,7 +1920,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCal
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringMessageCodec.java
|
||||
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user