blob: 8aae32668da0d2f515a3e9d0433a51b22440973a [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.yourcompany.channels;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Date;
import android.os.Bundle;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.*;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
DartExecutor dartExecutor = flutterEngine.getDartExecutor();
setupMessageHandshake(new BasicMessageChannel<>(dartExecutor, "binary-msg", BinaryCodec.INSTANCE));
setupMessageHandshake(new BasicMessageChannel<>(dartExecutor, "string-msg", StringCodec.INSTANCE));
setupMessageHandshake(new BasicMessageChannel<>(dartExecutor, "json-msg", JSONMessageCodec.INSTANCE));
setupMessageHandshake(new BasicMessageChannel<>(dartExecutor, "std-msg", ExtendedStandardMessageCodec.INSTANCE));
setupMethodHandshake(new MethodChannel(dartExecutor, "json-method", JSONMethodCodec.INSTANCE));
setupMethodHandshake(new MethodChannel(dartExecutor, "std-method", new StandardMethodCodec(ExtendedStandardMessageCodec.INSTANCE)));
}
private <T> void setupMessageHandshake(final BasicMessageChannel<T> channel) {
// On message receipt, do a send/reply/send round-trip in the other direction,
// then reply to the first message.
channel.setMessageHandler(new BasicMessageChannel.MessageHandler<T>() {
@Override
public void onMessage(final T message, final BasicMessageChannel.Reply<T> reply) {
final T messageEcho = echo(message);
channel.send(messageEcho, new BasicMessageChannel.Reply<T>() {
@Override
public void reply(T replyMessage) {
channel.send(echo(replyMessage));
reply.reply(messageEcho);
}
});
}
});
}
// Outgoing ByteBuffer messages must be direct-allocated and payload placed between
// position 0 and current position.
@SuppressWarnings("unchecked")
private <T> T echo(T message) {
if (message instanceof ByteBuffer) {
final ByteBuffer buffer = (ByteBuffer) message;
final ByteBuffer echo = ByteBuffer.allocateDirect(buffer.remaining());
echo.put(buffer);
return (T) echo;
}
return message;
}
private void setupMethodHandshake(final MethodChannel channel) {
channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(final MethodCall methodCall, final MethodChannel.Result result) {
switch (methodCall.method) {
case "success":
doSuccessHandshake(channel, methodCall, result);
break;
case "error":
doErrorHandshake(channel, methodCall, result);
break;
default:
doNotImplementedHandshake(channel, methodCall, result);
break;
}
}
});
}
private void doSuccessHandshake(final MethodChannel channel, final MethodCall methodCall, final MethodChannel.Result result) {
channel.invokeMethod(methodCall.method, methodCall.arguments, new MethodChannel.Result() {
@Override
public void success(Object o) {
channel.invokeMethod(methodCall.method, o);
result.success(methodCall.arguments);
}
@Override
public void error(String code, String message, Object details) {
throw new AssertionError("Should not be called");
}
@Override
public void notImplemented() {
throw new AssertionError("Should not be called");
}
});
}
private void doErrorHandshake(final MethodChannel channel, final MethodCall methodCall, final MethodChannel.Result result) {
channel.invokeMethod(methodCall.method, methodCall.arguments, new MethodChannel.Result() {
@Override
public void success(Object o) {
throw new AssertionError("Should not be called");
}
@Override
public void error(String code, String message, Object details) {
channel.invokeMethod(methodCall.method, details);
result.error(code, message, methodCall.arguments);
}
@Override
public void notImplemented() {
throw new AssertionError("Should not be called");
}
});
}
private void doNotImplementedHandshake(final MethodChannel channel, final MethodCall methodCall, final MethodChannel.Result result) {
channel.invokeMethod(methodCall.method, methodCall.arguments, new MethodChannel.Result() {
@Override
public void success(Object o) {
throw new AssertionError("Should not be called");
}
@Override
public void error(String code, String message, Object details) {
throw new AssertionError("Should not be called");
}
@Override
public void notImplemented() {
channel.invokeMethod(methodCall.method, null);
result.notImplemented();
}
});
}
}
final class ExtendedStandardMessageCodec extends StandardMessageCodec {
public static final ExtendedStandardMessageCodec INSTANCE = new ExtendedStandardMessageCodec();
private static final byte DATE = (byte) 128;
private static final byte PAIR = (byte) 129;
@Override
protected void writeValue(ByteArrayOutputStream stream, Object value) {
if (value instanceof Date) {
stream.write(DATE);
writeLong(stream, ((Date) value).getTime());
} else if (value instanceof Pair) {
stream.write(PAIR);
writeValue(stream, ((Pair) value).left);
writeValue(stream, ((Pair) value).right);
} else {
super.writeValue(stream, value);
}
}
@Override
protected Object readValueOfType(byte type, ByteBuffer buffer) {
switch (type) {
case DATE:
return new Date(buffer.getLong());
case PAIR:
return new Pair(readValue(buffer), readValue(buffer));
default: return super.readValueOfType(type, buffer);
}
}
}
final class Pair {
public final Object left;
public final Object right;
public Pair(Object left, Object right) {
this.left = left;
this.right = right;
}
@Override
public String toString() {
return "Pair[" + left + ", " + right + "]";
}
}