[pigeon] made it so async java handlers can report errors (#460)
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index f3cd21c..b91c59d 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.0.2
+
+* [java] Made it so `@async` handlers in `@HostApi()` can report errors
+ explicitly.
+
## 1.0.1
* [front-end] Fixed bug where classes only referenced as type arguments for
generics weren't being generated.
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 862170e..fea3f11 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -8,7 +8,7 @@
import 'ast.dart';
/// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '1.0.1';
+const String pigeonVersion = '1.0.2';
/// Read all the content from [stdin] to a String.
String readStdin() {
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index 3553703..2a4aac0 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -160,7 +160,9 @@
indent.scoped('{', '} else {', () {
indent.write('channel.setMessageHandler((message, reply) -> ');
indent.scoped('{', '});', () {
- final String returnType = _javaTypeForDartType(method.returnType);
+ final String returnType = method.returnType.isVoid
+ ? 'Void'
+ : _javaTypeForDartType(method.returnType);
indent.writeln('Map<String, Object> wrapped = new HashMap<>();');
indent.write('try ');
indent.scoped('{', '}', () {
@@ -184,12 +186,20 @@
if (method.isAsynchronous) {
final String resultValue =
method.returnType.isVoid ? 'null' : 'result';
- methodArgument.add(
- 'result -> { '
- 'wrapped.put("${Keys.result}", $resultValue); '
- 'reply.reply(wrapped); '
- '}',
- );
+ const String resultName = 'resultCallback';
+ indent.format('''
+Result<$returnType> $resultName = new Result<$returnType>() {
+\tpublic void success($returnType result) {
+\t\twrapped.put("${Keys.result}", $resultValue);
+\t\treply.reply(wrapped);
+\t}
+\tpublic void error(Throwable error) {
+\t\twrapped.put("${Keys.error}", wrapError(error));
+\t\treply.reply(wrapped);
+\t}
+};
+''');
+ methodArgument.add(resultName);
}
final String call =
'api.${method.name}(${methodArgument.join(', ')})';
@@ -493,6 +503,7 @@
indent.write('public interface Result<T> ');
indent.scoped('{', '}', () {
indent.writeln('void success(T result);');
+ indent.writeln('void error(Throwable error);');
});
}
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java
new file mode 100644
index 0000000..c8da07d
--- /dev/null
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java
@@ -0,0 +1,98 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package com.example.android_unit_tests;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.example.android_unit_tests.AsyncHandlers.*;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MessageCodec;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class AsyncTest {
+ class Success implements Api2Host {
+ @Override
+ public void calculate(Value value, Result<Value> result) {
+ result.success(value);
+ }
+
+ @Override
+ public void voidVoid(Result<Void> result) {
+ result.success(null);
+ }
+ }
+
+ class Error implements Api2Host {
+ @Override
+ public void calculate(Value value, Result<Value> result) {
+ result.error(new Exception("error"));
+ }
+
+ @Override
+ public void voidVoid(Result<Void> result) {
+ result.error(new Exception("error"));
+ }
+ }
+
+ @Test
+ public void asyncSuccess() {
+ Success api = new Success();
+ BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
+ Api2Host.setup(binaryMessenger, api);
+ ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler =
+ ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
+ verify(binaryMessenger).setMessageHandler(eq("dev.flutter.pigeon.Api2Host.calculate"), any());
+ verify(binaryMessenger)
+ .setMessageHandler(eq("dev.flutter.pigeon.Api2Host.voidVoid"), handler.capture());
+ MessageCodec<Object> codec = Pigeon.Api.getCodec();
+ ByteBuffer message = codec.encodeMessage(null);
+ Boolean[] didCall = {false};
+ handler
+ .getValue()
+ .onMessage(
+ message,
+ (bytes) -> {
+ bytes.rewind();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> wrapped = (Map<String, Object>) codec.decodeMessage(bytes);
+ assertTrue(wrapped.containsKey("result"));
+ didCall[0] = true;
+ });
+ assertTrue(didCall[0]);
+ }
+
+ @Test
+ public void asyncError() {
+ Error api = new Error();
+ BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
+ Api2Host.setup(binaryMessenger, api);
+ ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler =
+ ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
+ verify(binaryMessenger).setMessageHandler(eq("dev.flutter.pigeon.Api2Host.calculate"), any());
+ verify(binaryMessenger)
+ .setMessageHandler(eq("dev.flutter.pigeon.Api2Host.voidVoid"), handler.capture());
+ MessageCodec<Object> codec = Pigeon.Api.getCodec();
+ ByteBuffer message = codec.encodeMessage(null);
+ Boolean[] didCall = {false};
+ handler
+ .getValue()
+ .onMessage(
+ message,
+ (bytes) -> {
+ bytes.rewind();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> wrapped = (Map<String, Object>) codec.decodeMessage(bytes);
+ assertTrue(wrapped.containsKey("error"));
+ assertEquals(
+ "java.lang.Exception: error", ((Map) wrapped.get("error")).get("message"));
+ didCall[0] = true;
+ });
+ assertTrue(didCall[0]);
+ }
+}
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 1b49a05..99fa15f 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
repository: https://github.com/flutter/packages/tree/master/packages/pigeon
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 1.0.1 # This must match the version in lib/generator_tools.dart
+version: 1.0.2 # This must match the version in lib/generator_tools.dart
environment:
sdk: '>=2.12.0 <3.0.0'
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index 3ed6590..5fe056a 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -514,12 +514,10 @@
final String code = sink.toString();
expect(code, contains('public interface Api'));
expect(code, contains('public interface Result<T> {'));
+ expect(code, contains('void error(Throwable error);'));
expect(
code, contains('void doSomething(Input arg, Result<Output> result);'));
- expect(
- code,
- contains(
- 'api.doSomething(argArg, result -> { wrapped.put("result", result); reply.reply(wrapped); });'));
+ expect(code, contains('api.doSomething(argArg, resultCallback);'));
expect(code, contains('channel.setMessageHandler(null)'));
});