diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 4f8c47d2b847..7724e95d9ae6 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 22.7.4 + +* [dart] Fixes bug with multi-instance event channel support. + ## 22.7.3 * Adds compatibility with `analyzer` 7.x.*. diff --git a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart index c180a9d742ac..0759a36398e3 100644 --- a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart +++ b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart @@ -93,8 +93,8 @@ Stream streamEvents({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; } - const EventChannel streamEventsChannel = EventChannel( - 'dev.flutter.pigeon.pigeon_example_package.EventChannelMethods.streamEvents', + final EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_example_package.EventChannelMethods.streamEvents$instanceName', pigeonMethodCodec); return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { return event as PlatformEvent; diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 4f54839f8744..58553b93489f 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -556,8 +556,8 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; if (instanceName.isNotEmpty) { instanceName = '.\$instanceName'; } - const EventChannel ${func.name}Channel = - EventChannel('${makeChannelName(api, func, dartPackageName)}', $_pigeonMethodChannelCodec); + final EventChannel ${func.name}Channel = + EventChannel('${makeChannelName(api, func, dartPackageName)}\$instanceName', $_pigeonMethodChannelCodec); return ${func.name}Channel.receiveBroadcastStream().map((dynamic event) { return event as ${func.returnType.baseName}; }); diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index a64742711cbf..7e301b4b16b4 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -14,7 +14,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '22.7.3'; +const String pigeonVersion = '22.7.4'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/pigeons/event_channel_tests.dart b/packages/pigeon/pigeons/event_channel_tests.dart index d925a320dfe3..e089a60db7e5 100644 --- a/packages/pigeon/pigeons/event_channel_tests.dart +++ b/packages/pigeon/pigeons/event_channel_tests.dart @@ -141,4 +141,5 @@ class ClassEvent extends PlatformEvent { abstract class EventChannelMethods { int streamInts(); PlatformEvent streamEvents(); + int streamConsistentNumbers(); } diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 01115629506d..bf3b05f4052d 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -2865,6 +2865,7 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { TargetGenerator.kotlin, TargetGenerator.swift ]; + testWidgets('event channel sends continuous ints', (_) async { final Stream events = streamInts(); final List listEvents = await events.toList(); @@ -2912,6 +2913,24 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { }); await completer.future; }, skip: !eventChannelSupported.contains(targetGenerator)); + + testWidgets('event channels handle multiple instances', (_) async { + final Completer completer1 = Completer(); + final Completer completer2 = Completer(); + final Stream events1 = streamConsistentNumbers(instanceName: '1'); + final Stream events2 = streamConsistentNumbers(instanceName: '2'); + + events1.listen((int event) { + expect(event, 1); + }).onDone(() => completer1.complete()); + + events2.listen((int event) { + expect(event, 2); + }).onDone(() => completer2.complete()); + + await completer1.future; + await completer2.future; + }, skip: !eventChannelSupported.contains(targetGenerator)); } class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi { diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart index fe57f06e99f8..a681092d35a9 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart @@ -432,8 +432,8 @@ Stream streamInts({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; } - const EventChannel streamIntsChannel = EventChannel( - 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts', + final EventChannel streamIntsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamInts$instanceName', pigeonMethodCodec); return streamIntsChannel.receiveBroadcastStream().map((dynamic event) { return event as int; @@ -444,10 +444,24 @@ Stream streamEvents({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; } - const EventChannel streamEventsChannel = EventChannel( - 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents', + final EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamEvents$instanceName', pigeonMethodCodec); return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { return event as PlatformEvent; }); } + +Stream streamConsistentNumbers({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + final EventChannel streamConsistentNumbersChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamConsistentNumbers$instanceName', + pigeonMethodCodec); + return streamConsistentNumbersChannel + .receiveBroadcastStream() + .map((dynamic event) { + return event as int; + }); +} diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt index 08eb85b55ef7..7b36b0a4350a 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -470,3 +470,22 @@ abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + streamHandler: StreamConsistentNumbersStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamConsistentNumbers" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val internalStreamHandler = PigeonStreamHandler(streamHandler) + EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) + .setStreamHandler(internalStreamHandler) + } + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index f175076abcda..3177d6cdc8d5 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -31,6 +31,10 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { StreamEventsStreamHandler.register(binding.binaryMessenger, SendClass) StreamIntsStreamHandler.register(binding.binaryMessenger, SendInts) + StreamConsistentNumbersStreamHandler.register( + binding.binaryMessenger, SendConsistentNumbers(1), "1") + StreamConsistentNumbersStreamHandler.register( + binding.binaryMessenger, SendConsistentNumbers(2), "2") } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -927,3 +931,27 @@ object SendClass : StreamEventsStreamHandler() { handler.postDelayed(r, 10) } } + +class SendConsistentNumbers(private val numberToSend: Long) : + StreamConsistentNumbersStreamHandler() { + private val handler = Handler(Looper.getMainLooper()) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= 10) { + sink.endOfStream() + } else { + handler.post { + sink.success(numberToSend) + count++ + } + handler.postDelayed(this, 10) + } + } + } + handler.postDelayed(r, 10) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift index 09f682ece4b6..d2970947a2de 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -574,3 +574,21 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { channel.setStreamHandler(internalStreamHandler) } } + +class StreamConsistentNumbersStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamConsistentNumbersStreamHandler + ) { + var channelName = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamConsistentNumbers" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index 6b39a002a141..1ecd036cd1ee 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -31,6 +31,12 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { StreamIntsStreamHandler.register(with: binaryMessenger, streamHandler: SendInts()) StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: SendEvents()) + StreamConsistentNumbersStreamHandler.register( + with: binaryMessenger, instanceName: "1", + streamHandler: SendConsistentNumbers(numberToSend: 1)) + StreamConsistentNumbersStreamHandler.register( + with: binaryMessenger, instanceName: "2", + streamHandler: SendConsistentNumbers(numberToSend: 2)) proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() @@ -1272,6 +1278,33 @@ class SendEvents: StreamEventsStreamHandler { } } +class SendConsistentNumbers: StreamConsistentNumbersStreamHandler { + let numberToSend: Int64 + init(numberToSend: Int64) { + self.numberToSend = numberToSend + } + var timerActive = false + var timer: Timer? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + let numberThatWillBeSent: Int64 = numberToSend + var count: Int64 = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(numberThatWillBeSent) + count += 1 + if count >= 10 { + sink.endOfStream() + self.timer?.invalidate() + } + } + } + } + } +} + class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) -> PigeonApiProxyApiTestClass diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift index 09f682ece4b6..d2970947a2de 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -574,3 +574,21 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { channel.setStreamHandler(internalStreamHandler) } } + +class StreamConsistentNumbersStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + streamHandler: StreamConsistentNumbersStreamHandler + ) { + var channelName = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelMethods.streamConsistentNumbers" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(internalStreamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index bcee6f7eb382..c23056111ed4 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -27,6 +27,12 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixOne") flutterSmallApiTwo = FlutterSmallApi( binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixTwo") + StreamConsistentNumbersStreamHandler.register( + with: binaryMessenger, instanceName: "1", + streamHandler: SendConsistentNumbers(numberToSend: 1)) + StreamConsistentNumbersStreamHandler.register( + with: binaryMessenger, instanceName: "2", + streamHandler: SendConsistentNumbers(numberToSend: 2)) proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() @@ -1270,6 +1276,33 @@ class SendEvents: StreamEventsStreamHandler { } } +class SendConsistentNumbers: StreamConsistentNumbersStreamHandler { + let numberToSend: Int64 + init(numberToSend: Int64) { + self.numberToSend = numberToSend + } + var timerActive = false + var timer: Timer? + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + let numberThatWillBeSent: Int64 = numberToSend + var count: Int64 = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(numberThatWillBeSent) + count += 1 + if count >= 10 { + sink.endOfStream() + self.timer?.invalidate() + } + } + } + } + } +} + class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) -> PigeonApiProxyApiTestClass diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 442e7194e9ba..021284ad7f9a 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 22.7.3 # This must match the version in lib/generator_tools.dart +version: 22.7.4 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.4.0