forked from flutter/flutter
-
Notifications
You must be signed in to change notification settings - Fork 2
/
plugin_event_channel.dart
153 lines (137 loc) · 5.1 KB
/
plugin_event_channel.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// 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.
// @dart = 2.8
import 'dart:async';
import 'package:flutter/services.dart';
import 'plugin_registry.dart';
/// A named channel for sending events to the framework-side using streams.
///
/// This is the platform-side equivalent of [EventChannel]. Whereas
/// [EventChannel] receives a stream of events from platform plugins, this
/// channel sends a stream of events to the handler listening on the
/// framework-side.
///
/// The channel [name] must not be null. If no [codec] is provided, then
/// [StandardMethodCodec] is used. If no [binaryMessenger] is provided, then
/// [pluginBinaryMessenger], which sends messages to the framework-side,
/// is used.
///
/// Channels created using this class implement two methods for
/// subscribing to the event stream. The methods use the encoding of
/// the specified [codec].
///
/// The first method is `listen`. When called, it begins forwarding
/// messages to the framework side when they are added to the
/// [controller]. This triggers the [onListen] callback on the
/// [controller].
///
/// The other method is `cancel`. When called, it stops forwarding
/// events to the framework. This triggers the [onCancel] callback on
/// the [controller].
///
/// Events added to the [controller] when the framework is not
/// subscribed are silently discarded.
class PluginEventChannel<T> {
/// Creates a new plugin event channel.
///
/// The [name] and [codec] arguments must not be null.
const PluginEventChannel(
this.name, [
this.codec = const StandardMethodCodec(),
this.binaryMessenger,
]) : assert(name != null),
assert(codec != null);
/// The logical channel on which communication happens.
///
/// This must not be null.
final String name;
/// The message codec used by this channel.
///
/// This must not be null. This defaults to [StandardMethodCodec].
final MethodCodec codec;
/// The messenger used by this channel to send platform messages.
///
/// When this is null, the [pluginBinaryMessenger] is used instead,
/// which sends messages from the platform-side to the
/// framework-side.
final BinaryMessenger binaryMessenger;
/// Use [setController] instead.
///
/// This setter is deprecated because it has no corresponding getter,
/// and providing a getter would require making this class non-const.
@Deprecated(
'Replace calls to the "controller" setter with calls to the "setController" method. '
'This feature was deprecated after v1.23.0-7.0.pre.'
)
set controller(StreamController<T> controller) {
setController(controller);
}
/// Changes the stream controller for this event channel.
///
/// Setting the controller to null disconnects from the channel (setting
/// the message handler on the [binaryMessenger] to null).
void setController(StreamController<T> controller) {
final BinaryMessenger messenger = binaryMessenger ?? pluginBinaryMessenger;
if (controller == null) {
messenger.setMessageHandler(name, null);
} else {
// The handler object is kept alive via its handle() method
// keeping a reference to itself. Ideally we would keep a
// reference to it so that there was a clear ownership model,
// but that would require making this class non-const. Having
// this class be const is convenient since it allows references
// to be obtained by using the constructor rather than having
// to literally pass references around.
final _EventChannelHandler<T> handler = _EventChannelHandler<T>(
name,
codec,
controller,
messenger,
);
messenger.setMessageHandler(name, handler.handle);
}
}
}
class _EventChannelHandler<T> {
_EventChannelHandler(this.name, this.codec, this.controller, this.messenger) : assert(messenger != null);
final String name;
final MethodCodec codec;
final StreamController<T> controller;
final BinaryMessenger messenger;
StreamSubscription<T> subscription;
Future<ByteData> handle(ByteData message) {
final MethodCall call = codec.decodeMethodCall(message);
switch (call.method) {
case 'listen':
assert(call.arguments == null);
return _listen();
case 'cancel':
assert(call.arguments == null);
return _cancel();
}
return null;
}
Future<ByteData> _listen() async {
if (subscription != null) {
await subscription.cancel();
}
subscription = controller.stream.listen((dynamic event) {
messenger.send(name, codec.encodeSuccessEnvelope(event));
}, onError: (dynamic error) {
messenger.send(name, codec.encodeErrorEnvelope(code: 'error', message: '$error'));
});
return codec.encodeSuccessEnvelope(null);
}
Future<ByteData> _cancel() async {
if (subscription == null) {
return codec.encodeErrorEnvelope(
code: 'error',
message: 'No active subscription to cancel.',
);
}
await subscription.cancel();
subscription = null;
return codec.encodeSuccessEnvelope(null);
}
}