| // 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 + "]"; |
| } |
| } |