Skip to content
This repository was archived by the owner on Sep 21, 2021. It is now read-only.

Commit a18a316

Browse files
committedJan 15, 2020
feature(multi instance):support multiple pusher instances
1 parent b12ea1c commit a18a316

20 files changed

+1121
-387
lines changed
 

‎.editorconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ end_of_line = lf
1111
insert_final_newline = true
1212
trim_trailing_whitespace = true
1313

14-
[*.java]
14+
[*{.java,.swift}]
1515
indent_size = 4
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,29 @@
11
package com.github.heywhy.flutter_pusher;
22

3-
import android.os.Handler;
43
import android.util.Log;
5-
import android.os.Looper;
6-
7-
import com.github.heywhy.flutter_pusher.listeners.*;
8-
4+
import com.github.heywhy.flutter_pusher.platform_messages.InstanceMessage;
95
import com.google.gson.Gson;
106
import com.google.gson.reflect.TypeToken;
11-
import com.pusher.client.Pusher;
12-
import com.pusher.client.PusherOptions;
13-
import com.pusher.client.channel.Channel;
14-
import com.pusher.client.connection.ConnectionEventListener;
15-
import com.pusher.client.connection.ConnectionState;
16-
import com.pusher.client.connection.ConnectionStateChange;
17-
import com.pusher.client.util.ConnectionFactory;
18-
import com.pusher.client.util.HttpAuthorizer;
19-
import com.pusher.client.util.UrlEncodedConnectionFactory;
20-
21-
import org.json.JSONObject;
22-
23-
import java.lang.reflect.Type;
24-
import java.util.HashMap;
25-
import java.util.Map;
26-
277
import io.flutter.plugin.common.EventChannel;
288
import io.flutter.plugin.common.MethodCall;
299
import io.flutter.plugin.common.MethodChannel;
3010
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
3111
import io.flutter.plugin.common.MethodChannel.Result;
3212
import io.flutter.plugin.common.PluginRegistry.Registrar;
3313

14+
import java.lang.reflect.Type;
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
3418
/**
3519
* FlutterPusherPlugin
3620
*/
3721
public class FlutterPusherPlugin implements MethodCallHandler {
3822

3923
public static String TAG = "FlutterPusherPlugin";
4024
public static EventChannel.EventSink eventSink;
41-
public static boolean isLoggingEnabled = false;
42-
43-
private Pusher pusher;
44-
private Map<String, Channel> channels = new HashMap<>();
4525

46-
private static EventChannelListener eventListener;
47-
private static PrivateChannelListener eventListenerPrivate;
48-
private static PresenceChannelListener eventListenerPresence;
26+
private Map<String, PusherInstance> pusherInstanceMap = new HashMap<>();
4927

5028
/**
5129
* Plugin registration.
@@ -54,10 +32,6 @@ public static void registerWith(Registrar registrar) {
5432
final MethodChannel channel = new MethodChannel(registrar.messenger(), "com.github.heywhy/pusher");
5533
final EventChannel eventStream = new EventChannel(registrar.messenger(), "com.github.heywhy/pusherStream");
5634

57-
eventListener = new EventChannelListener();
58-
eventListenerPrivate = new PrivateChannelListener();
59-
eventListenerPresence = new PresenceChannelListener();
60-
6135
channel.setMethodCallHandler(new FlutterPusherPlugin());
6236
eventStream.setStreamHandler(new EventChannel.StreamHandler() {
6337
@Override
@@ -74,296 +48,22 @@ public void onCancel(Object args) {
7448

7549
@Override
7650
public void onMethodCall(MethodCall call, Result result) {
77-
switch (call.method) {
78-
case "init":
79-
init(call, result);
80-
break;
81-
case "connect":
82-
connect(call, result);
83-
break;
84-
case "disconnect":
85-
disconnect(call, result);
86-
break;
87-
case "subscribe":
88-
subscribe(call, result);
89-
break;
90-
case "unsubscribe":
91-
unsubscribe(call, result);
92-
break;
93-
case "bind":
94-
bind(call, result);
95-
break;
96-
case "unbind":
97-
unbind(call, result);
98-
break;
99-
case "trigger":
100-
// trigger(call, result);
Has a conversation. Original line has a conversation.
101-
break;
102-
default:
103-
result.notImplemented();
104-
break;
51+
Type type = new TypeToken<InstanceMessage>(){}.getType();
52+
InstanceMessage instanceMessage = new Gson().fromJson(call.arguments.toString(), type);
53+
String instanceId = instanceMessage.getInstanceId();
54+
PusherInstance instance = getPusherInstance(instanceId);
55+
if (instance == null) {
56+
String message = String.format("Instance with id %s not found", instanceId);
57+
throw new IllegalArgumentException(message);
10558
}
106-
}
107-
108-
109-
private void init(MethodCall call, Result result) {
110-
if (pusher != null) {
111-
for (Map.Entry<String, Channel> entry : channels.entrySet()) {
112-
String name = entry.getKey();
113-
pusher.unsubscribe(name);
114-
channels.remove(name);
115-
}
116-
}
117-
118-
try {
119-
final JSONObject json = new JSONObject(call.arguments.toString());
120-
final JSONObject options = json.getJSONObject("options");
121-
122-
if (json.has("isLoggingEnabled")) {
123-
isLoggingEnabled = json.getBoolean("isLoggingEnabled");
124-
}
12559

126-
// setup options
127-
final PusherOptions pusherOptions = new PusherOptions();
128-
129-
if (options.has("auth")) {
130-
final JSONObject auth = options.getJSONObject("auth");
131-
final String endpoint = auth.getString("endpoint");
132-
final Type mapType = new TypeToken<Map<String, String>>() {}.getType();
133-
final Map<String, String> headers = new Gson().fromJson(auth.get("headers").toString(), mapType);
134-
135-
pusherOptions.setAuthorizer(getAuthorizer(endpoint, headers));
136-
}
137-
138-
if (options.has("activityTimeout")) {
139-
pusherOptions.setActivityTimeout(options.getInt("activityTimeout"));
140-
}
141-
if (options.has("cluster")) {
142-
pusherOptions.setCluster(options.getString("cluster"));
143-
}
144-
if (options.has("host")) {
145-
pusherOptions.setHost(options.getString("host"));
146-
}
147-
148-
// defaults to encrypted connection on port 443
149-
final int port = options.has("port") ? options.getInt("port") : 443;
150-
final boolean encrypted = !options.has("encrypted") || options.getBoolean("encrypted");
151-
152-
if (encrypted) {
153-
pusherOptions.setWssPort(port);
154-
} else {
155-
pusherOptions.setWsPort(port);
156-
}
157-
pusherOptions.setEncrypted(encrypted);
158-
159-
160-
// create client
161-
pusher = new Pusher(json.getString("appKey"), pusherOptions);
162-
163-
if (isLoggingEnabled) {
164-
Log.d(TAG, "init");
165-
}
166-
result.success(null);
167-
} catch (Exception e) {
168-
if (isLoggingEnabled) {
169-
Log.d(TAG, "init error: " + e.getMessage());
170-
e.printStackTrace();
171-
}
172-
}
60+
instance.onMethodCall(call, result);
17361
}
17462

175-
private void connect(MethodCall call, Result result) {
176-
pusher.connect(new ConnectionEventListener() {
177-
@Override
178-
public void onConnectionStateChange(final ConnectionStateChange change) {
179-
new Handler(Looper.getMainLooper()).post(new Runnable() {
180-
@Override
181-
public void run() {
182-
try {
183-
final JSONObject eventStreamMessageJson = new JSONObject();
184-
final JSONObject connectionStateChangeJson = new JSONObject();
185-
connectionStateChangeJson.put("currentState", change.getCurrentState().toString());
186-
connectionStateChangeJson.put("previousState", change.getPreviousState().toString());
187-
eventStreamMessageJson.put("connectionStateChange", connectionStateChangeJson);
188-
eventSink.success(eventStreamMessageJson.toString());
189-
} catch (Exception e) {
190-
if (isLoggingEnabled) {
191-
Log.d(TAG, "onConnectionStateChange error: " + e.getMessage());
192-
e.printStackTrace();
193-
}
194-
}
195-
}
196-
});
197-
}
198-
199-
@Override
200-
public void onError(final String message, final String code, final Exception ex) {
201-
new Handler(Looper.getMainLooper()).post(new Runnable() {
202-
@Override
203-
public void run() {
204-
try {
205-
final String exMessage = ex != null ? ex.getMessage() : null;
206-
final JSONObject eventStreamMessageJson = new JSONObject();
207-
final JSONObject connectionErrorJson = new JSONObject();
208-
209-
connectionErrorJson.put("message", message);
210-
connectionErrorJson.put("code", code);
211-
connectionErrorJson.put("exception", exMessage);
212-
eventStreamMessageJson.put("connectionError", connectionErrorJson);
213-
214-
eventSink.success(eventStreamMessageJson.toString());
215-
216-
} catch (Exception e) {
217-
if (isLoggingEnabled) {
218-
Log.d(TAG, "onError exception: " + e.getMessage());
219-
e.printStackTrace();
220-
}
221-
}
222-
}
223-
});
224-
}
225-
226-
}, ConnectionState.ALL);
227-
228-
if (isLoggingEnabled) {
229-
Log.d(TAG, "connect");
230-
}
231-
result.success(null);
232-
}
233-
234-
235-
private void disconnect(MethodCall call, Result result) {
236-
pusher.disconnect();
237-
if (isLoggingEnabled) {
238-
Log.d(TAG, "disconnect");
239-
}
240-
result.success(null);
241-
}
242-
243-
private void subscribe(MethodCall call, Result result) {
244-
final String channelName = call.arguments.toString();
245-
final String channelType = channelName.split("-")[0];
246-
Channel channel = channels.get(channelName);
247-
248-
if (channel != null && channel.isSubscribed()) {
249-
if (isLoggingEnabled) {
250-
Log.d(TAG, "Already subscribed, ignoring ...");
251-
}
252-
result.success(null);
253-
return;
254-
}
255-
256-
switch (channelType) {
257-
case "private":
258-
channel = pusher.subscribePrivate(channelName, eventListenerPrivate);
259-
if (isLoggingEnabled) {
260-
Log.d(TAG, "subscribe (private)");
261-
}
262-
break;
263-
case "presence":
264-
channel = pusher.subscribePresence(channelName, eventListenerPresence);
265-
if (isLoggingEnabled) {
266-
Log.d(TAG, "subscribe (presence)");
267-
}
268-
break;
269-
default:
270-
channel = pusher.subscribe(channelName, eventListener);
271-
272-
if (isLoggingEnabled) {
273-
Log.d(TAG, "subscribe");
274-
}
275-
break;
63+
private PusherInstance getPusherInstance(String instanceId) {
64+
if (instanceId != null && !pusherInstanceMap.containsKey(instanceId)) {
65+
pusherInstanceMap.put(instanceId, new PusherInstance(instanceId));
27666
}
277-
278-
channels.put(channelName, channel);
279-
result.success(null);
280-
}
281-
282-
private void unsubscribe(MethodCall call, Result result) {
283-
final String channelName = call.arguments.toString();
284-
pusher.unsubscribe(call.arguments.toString());
285-
channels.remove(channelName);
286-
287-
if (isLoggingEnabled) {
288-
Log.d(TAG, String.format("unsubscribe (%s)", channelName));
289-
}
290-
result.success(null);
291-
}
292-
293-
private void bind(MethodCall call, Result result) {
294-
try {
295-
final JSONObject json = new JSONObject(call.arguments.toString());
296-
final String channelName = json.getString("channelName");
297-
final String channelType = channelName.split("-")[0];
298-
final String eventName = json.getString("eventName");
299-
300-
Channel channel = channels.get(channelName);
301-
302-
switch (channelType) {
303-
case "private":
304-
channel.bind(eventName, eventListenerPrivate);
305-
break;
306-
case "presence":
307-
channel.bind(eventName, eventListenerPresence);
308-
break;
309-
default:
310-
channel.bind(eventName, eventListener);
311-
break;
312-
}
313-
314-
if (isLoggingEnabled) {
315-
Log.d(TAG, String.format("bind (%s)", eventName));
316-
}
317-
result.success(null);
318-
} catch (Exception e) {
319-
if (isLoggingEnabled) {
320-
Log.d(TAG, String.format("bind exception: %s", e.getMessage()));
321-
e.printStackTrace();
322-
}
323-
}
324-
}
325-
326-
private void unbind(MethodCall call, Result result) {
327-
try {
328-
final JSONObject json = new JSONObject(call.arguments.toString());
329-
final String channelName = json.getString("channelName");
330-
final String channelType = channelName.split("-")[0];
331-
final String eventName = json.getString("eventName");
332-
333-
Channel channel = channels.get(channelName);
334-
switch (channelType) {
335-
case "private":
336-
channel.unbind(eventName, eventListenerPrivate);
337-
break;
338-
case "presence":
339-
channel.unbind(eventName, eventListenerPresence);
340-
break;
341-
default:
342-
channel.unbind(eventName, eventListener);
343-
break;
344-
}
345-
346-
if (isLoggingEnabled) {
347-
Log.d(TAG, String.format("unbind (%s)", eventName));
348-
}
349-
result.success(null);
350-
} catch (Exception e) {
351-
if (isLoggingEnabled) {
352-
Log.d(TAG, String.format("unbind exception: %s", e.getMessage()));
353-
e.printStackTrace();
354-
}
355-
}
356-
}
357-
358-
private HttpAuthorizer getAuthorizer(String endpoint, Map<String, String> headers) {
359-
final ConnectionFactory connection = headers.containsValue("application/json")
360-
? new JsonEncodedConnectionFactory()
361-
: new UrlEncodedConnectionFactory();
362-
363-
final HttpAuthorizer authorizer = new HttpAuthorizer(endpoint, connection);
364-
authorizer.setHeaders(headers);
365-
366-
return authorizer;
367-
67+
return pusherInstanceMap.get(instanceId);
36868
}
36969
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
package com.github.heywhy.flutter_pusher;
2+
3+
import android.os.Handler;
4+
import android.os.Looper;
5+
import android.util.Log;
6+
import com.github.heywhy.flutter_pusher.listeners.EventChannelListener;
7+
import com.github.heywhy.flutter_pusher.listeners.PresenceChannelListener;
8+
import com.github.heywhy.flutter_pusher.listeners.PrivateChannelListener;
9+
import com.google.gson.Gson;
10+
import com.google.gson.reflect.TypeToken;
11+
import com.pusher.client.Pusher;
12+
import com.pusher.client.PusherOptions;
13+
import com.pusher.client.channel.Channel;
14+
import com.pusher.client.connection.ConnectionEventListener;
15+
import com.pusher.client.connection.ConnectionState;
16+
import com.pusher.client.connection.ConnectionStateChange;
17+
import com.pusher.client.util.ConnectionFactory;
18+
import com.pusher.client.util.HttpAuthorizer;
19+
import com.pusher.client.util.UrlEncodedConnectionFactory;
20+
import io.flutter.plugin.common.MethodCall;
21+
import io.flutter.plugin.common.MethodChannel;
22+
import org.json.JSONObject;
23+
24+
import java.lang.reflect.Type;
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
28+
import static com.github.heywhy.flutter_pusher.FlutterPusherPlugin.TAG;
29+
import static com.github.heywhy.flutter_pusher.FlutterPusherPlugin.eventSink;
30+
31+
public class PusherInstance implements MethodChannel.MethodCallHandler {
32+
33+
private Pusher pusher;
34+
private String instanceId;
35+
private boolean isLoggingEnabled = false;
36+
private Map<String, Channel> channels = new HashMap<>();
37+
38+
private EventChannelListener eventListener;
39+
private PrivateChannelListener eventListenerPrivate;
40+
private PresenceChannelListener eventListenerPresence;
41+
42+
PusherInstance(String instanceId) {
43+
this.instanceId = instanceId;
44+
}
45+
46+
@Override
47+
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
48+
switch (call.method) {
49+
case "init":
50+
init(call, result);
51+
break;
52+
case "connect":
53+
connect(call, result);
54+
break;
55+
case "disconnect":
56+
disconnect(call, result);
57+
break;
58+
case "subscribe":
59+
subscribe(call, result);
60+
break;
61+
case "unsubscribe":
62+
unsubscribe(call, result);
63+
break;
64+
case "bind":
65+
bind(call, result);
66+
break;
67+
case "unbind":
68+
unbind(call, result);
69+
break;
70+
case "trigger":
71+
// trigger(call, result);
72+
break;
73+
case "getSocketId":
74+
getSocketId(call, result);
75+
break;
76+
default:
77+
result.notImplemented();
78+
break;
79+
}
80+
}
81+
82+
private void initListeners() {
83+
eventListener = new EventChannelListener(instanceId, isLoggingEnabled);
84+
eventListenerPrivate = new PrivateChannelListener(instanceId, isLoggingEnabled);
85+
eventListenerPresence = new PresenceChannelListener(instanceId, isLoggingEnabled);
86+
}
87+
88+
private void init(MethodCall call, MethodChannel.Result result) {
89+
if (pusher != null) {
90+
for (Map.Entry<String, Channel> entry : channels.entrySet()) {
91+
String name = entry.getKey();
92+
pusher.unsubscribe(name);
93+
channels.remove(name);
94+
}
95+
}
96+
97+
try {
98+
final JSONObject json = new JSONObject(call.arguments.toString());
99+
final JSONObject options = json.getJSONObject("options");
100+
101+
if (json.has("isLoggingEnabled")) {
102+
isLoggingEnabled = json.getBoolean("isLoggingEnabled");
103+
}
104+
105+
// setup options
106+
final PusherOptions pusherOptions = new PusherOptions();
107+
108+
if (options.has("auth")) {
109+
final JSONObject auth = options.getJSONObject("auth");
110+
final String endpoint = auth.getString("endpoint");
111+
final Type mapType = new TypeToken<Map<String, String>>() {}.getType();
112+
final Map<String, String> headers = new Gson().fromJson(auth.get("headers").toString(), mapType);
113+
114+
pusherOptions.setAuthorizer(getAuthorizer(endpoint, headers));
115+
}
116+
117+
if (options.has("activityTimeout")) {
118+
pusherOptions.setActivityTimeout(options.getInt("activityTimeout"));
119+
}
120+
if (options.has("cluster")) {
121+
pusherOptions.setCluster(options.getString("cluster"));
122+
}
123+
if (options.has("host")) {
124+
pusherOptions.setHost(options.getString("host"));
125+
}
126+
127+
// defaults to encrypted connection on port 443
128+
final int port = options.has("port") ? options.getInt("port") : 443;
129+
final boolean encrypted = !options.has("encrypted") || options.getBoolean("encrypted");
130+
131+
if (encrypted) {
132+
pusherOptions.setWssPort(port);
133+
} else {
134+
pusherOptions.setWsPort(port);
135+
}
136+
pusherOptions.setEncrypted(encrypted);
137+
138+
139+
// create client
140+
pusher = new Pusher(json.getString("appKey"), pusherOptions);
141+
initListeners();
142+
143+
if (isLoggingEnabled) {
144+
Log.d(TAG, "init");
145+
}
146+
result.success(null);
147+
} catch (Exception e) {
148+
if (isLoggingEnabled) {
149+
Log.d(TAG, "init error: " + e.getMessage());
150+
e.printStackTrace();
151+
}
152+
}
153+
}
154+
155+
private void connect(MethodCall call, MethodChannel.Result result) {
156+
pusher.connect(new ConnectionEventListener() {
157+
@Override
158+
public void onConnectionStateChange(final ConnectionStateChange change) {
159+
new Handler(Looper.getMainLooper()).post(new Runnable() {
160+
@Override
161+
public void run() {
162+
try {
163+
final JSONObject eventStreamMessageJson = new JSONObject();
164+
final JSONObject connectionStateChangeJson = new JSONObject();
165+
166+
connectionStateChangeJson.put("currentState", change.getCurrentState().toString());
167+
connectionStateChangeJson.put("previousState", change.getPreviousState().toString());
168+
eventStreamMessageJson.put("connectionStateChange", connectionStateChangeJson);
169+
eventStreamMessageJson.put("instanceId", instanceId);
170+
eventSink.success(eventStreamMessageJson.toString());
171+
} catch (Exception e) {
172+
if (isLoggingEnabled) {
173+
Log.d(TAG, "onConnectionStateChange error: " + e.getMessage());
174+
e.printStackTrace();
175+
}
176+
}
177+
}
178+
});
179+
}
180+
181+
@Override
182+
public void onError(final String message, final String code, final Exception ex) {
183+
new Handler(Looper.getMainLooper()).post(new Runnable() {
184+
@Override
185+
public void run() {
186+
try {
187+
final String exMessage = ex != null ? ex.getMessage() : null;
188+
final JSONObject eventStreamMessageJson = new JSONObject();
189+
final JSONObject connectionErrorJson = new JSONObject();
190+
191+
connectionErrorJson.put("instanceId", instanceId);
192+
connectionErrorJson.put("message", message);
193+
connectionErrorJson.put("code", code);
194+
connectionErrorJson.put("exception", exMessage);
195+
eventStreamMessageJson.put("connectionError", connectionErrorJson);
196+
eventStreamMessageJson.put("instanceId", instanceId);
197+
198+
eventSink.success(eventStreamMessageJson.toString());
199+
200+
} catch (Exception e) {
201+
if (isLoggingEnabled) {
202+
Log.d(TAG, "onError exception: " + e.getMessage());
203+
e.printStackTrace();
204+
}
205+
}
206+
}
207+
});
208+
}
209+
210+
}, ConnectionState.ALL);
211+
212+
if (isLoggingEnabled) {
213+
Log.d(TAG, "connect");
214+
}
215+
result.success(null);
216+
}
217+
218+
219+
private void getSocketId(MethodCall call, MethodChannel.Result result) {
220+
result.success(pusher.getConnection().getSocketId());
221+
}
222+
223+
private void disconnect(MethodCall call, MethodChannel.Result result) {
224+
pusher.disconnect();
225+
if (isLoggingEnabled) {
226+
Log.d(TAG, "disconnect");
227+
}
228+
result.success(null);
229+
}
230+
231+
private void subscribe(MethodCall call, MethodChannel.Result result) {
232+
233+
try {
234+
final JSONObject json = new JSONObject(call.arguments.toString());
235+
final String channelName = json.getString("channelName");
236+
final String channelType = channelName.split("-")[0];
237+
Channel channel = channels.get(channelName);
238+
239+
if (channel != null && channel.isSubscribed()) {
240+
if (isLoggingEnabled) {
241+
Log.d(TAG, "Already subscribed, ignoring ...");
242+
}
243+
result.success(null);
244+
return;
245+
}
246+
247+
switch (channelType) {
248+
case "private":
249+
channel = pusher.subscribePrivate(channelName, eventListenerPrivate);
250+
if (isLoggingEnabled) {
251+
Log.d(TAG, "subscribe (private)");
252+
}
253+
break;
254+
case "presence":
255+
channel = pusher.subscribePresence(channelName, eventListenerPresence);
256+
if (isLoggingEnabled) {
257+
Log.d(TAG, "subscribe (presence)");
258+
}
259+
break;
260+
default:
261+
channel = pusher.subscribe(channelName, eventListener);
262+
263+
if (isLoggingEnabled) {
264+
Log.d(TAG, "subscribe");
265+
}
266+
break;
267+
}
268+
269+
channels.put(channelName, channel);
270+
result.success(null);
271+
} catch (Exception e) {
272+
if (isLoggingEnabled) {
273+
Log.d(TAG, "subscribe error: " + e.getMessage());
274+
e.printStackTrace();
275+
}
276+
}
277+
278+
}
279+
280+
private void unsubscribe(MethodCall call, MethodChannel.Result result) {
281+
try {
282+
final JSONObject json = new JSONObject(call.arguments.toString());
283+
final String channelName = json.getString("channelName");
284+
pusher.unsubscribe(channelName);
285+
channels.remove(channelName);
286+
287+
if (isLoggingEnabled) {
288+
Log.d(TAG, String.format("unsubscribe (%s)", channelName));
289+
}
290+
result.success(null);
291+
} catch (Exception e) {
292+
if (isLoggingEnabled) {
293+
Log.d(TAG, "unsubscribe error: " + e.getMessage());
294+
e.printStackTrace();
295+
}
296+
}
297+
298+
}
299+
300+
private void bind(MethodCall call, MethodChannel.Result result) {
301+
try {
302+
final JSONObject json = new JSONObject(call.arguments.toString());
303+
final String channelName = json.getString("channelName");
304+
final String channelType = channelName.split("-")[0];
305+
final String eventName = json.getString("eventName");
306+
307+
Channel channel = channels.get(channelName);
308+
309+
switch (channelType) {
310+
case "private":
311+
channel.bind(eventName, eventListenerPrivate);
312+
break;
313+
case "presence":
314+
channel.bind(eventName, eventListenerPresence);
315+
break;
316+
default:
317+
channel.bind(eventName, eventListener);
318+
break;
319+
}
320+
321+
if (isLoggingEnabled) {
322+
Log.d(TAG, String.format("bind (%s)", eventName));
323+
}
324+
result.success(null);
325+
} catch (Exception e) {
326+
if (isLoggingEnabled) {
327+
Log.d(TAG, String.format("bind exception: %s", e.getMessage()));
328+
e.printStackTrace();
329+
}
330+
}
331+
}
332+
333+
private void unbind(MethodCall call, MethodChannel.Result result) {
334+
try {
335+
final JSONObject json = new JSONObject(call.arguments.toString());
336+
final String channelName = json.getString("channelName");
337+
final String channelType = channelName.split("-")[0];
338+
final String eventName = json.getString("eventName");
339+
340+
Channel channel = channels.get(channelName);
341+
switch (channelType) {
342+
case "private":
343+
channel.unbind(eventName, eventListenerPrivate);
344+
break;
345+
case "presence":
346+
channel.unbind(eventName, eventListenerPresence);
347+
break;
348+
default:
349+
channel.unbind(eventName, eventListener);
350+
break;
351+
}
352+
353+
if (isLoggingEnabled) {
354+
Log.d(TAG, String.format("unbind (%s)", eventName));
355+
}
356+
result.success(null);
357+
} catch (Exception e) {
358+
if (isLoggingEnabled) {
359+
Log.d(TAG, String.format("unbind exception: %s", e.getMessage()));
360+
e.printStackTrace();
361+
}
362+
}
363+
}
364+
365+
private HttpAuthorizer getAuthorizer(String endpoint, Map<String, String> headers) {
366+
final ConnectionFactory connection = headers.containsValue("application/json")
367+
? new JsonEncodedConnectionFactory()
368+
: new UrlEncodedConnectionFactory();
369+
370+
final HttpAuthorizer authorizer = new HttpAuthorizer(endpoint, connection);
371+
authorizer.setHeaders(headers);
372+
373+
return authorizer;
374+
375+
}
376+
}

‎android/src/main/java/com/github/heywhy/flutter_pusher/listeners/EventChannelListener.java

+22-10
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import android.util.Log;
55
import android.os.Looper;
66

7-
import com.github.heywhy.flutter_pusher.FlutterPusherPlugin;
8-
97
import com.pusher.client.channel.ChannelEventListener;
108
import com.pusher.client.channel.PusherEvent;
119

@@ -14,11 +12,22 @@
1412

1513
import org.json.JSONObject;
1614

15+
import static com.github.heywhy.flutter_pusher.FlutterPusherPlugin.TAG;
16+
import static com.github.heywhy.flutter_pusher.FlutterPusherPlugin.eventSink;
17+
1718
public class EventChannelListener implements ChannelEventListener {
1819
static final String SUBSCRIPTION_SUCCESS_EVENT = "pusher:subscription_succeeded";
1920
static final String MEMBER_ADDED_EVENT = "pusher:member_added";
2021
static final String MEMBER_REMOVED_EVENT = "pusher:member_removed";
2122

23+
private String instanceId;
24+
private boolean isLoggingEnabled;
25+
26+
public EventChannelListener(String instanceId, boolean isLoggingEnabled) {
27+
this.instanceId = instanceId;
28+
this.isLoggingEnabled = isLoggingEnabled;
29+
}
30+
2231
static PusherEvent toPusherEvent(String channel, String event, String userId, String data) {
2332
final Map<String, Object> eventData = new HashMap<>();
2433

@@ -47,12 +56,14 @@ public void run() {
4756
eventJson.put("channel", channel);
4857
eventJson.put("event", event);
4958
eventJson.put("data", data);
59+
eventStreamMessageJson.put("isEvent", true);
5060
eventStreamMessageJson.put("event", eventJson);
61+
eventStreamMessageJson.put("instanceId", instanceId);
5162

52-
FlutterPusherPlugin.eventSink.success(eventStreamMessageJson.toString());
63+
eventSink.success(eventStreamMessageJson.toString());
5364

54-
if (FlutterPusherPlugin.isLoggingEnabled) {
55-
Log.d(FlutterPusherPlugin.TAG, String.format("onEvent: \nCHANNEL: %s \nEVENT: %s \nDATA: %s", channel, event, data));
65+
if (isLoggingEnabled) {
66+
Log.d(TAG, String.format("onEvent: \nCHANNEL: %s \nEVENT: %s \nDATA: %s", channel, event, data));
5667
}
5768
} catch (Exception e) {
5869
onError(e);
@@ -73,16 +84,17 @@ public void run() {
7384
connectionErrorJson.put("code", "Channel error");
7485
connectionErrorJson.put("exception", e);
7586
eventStreamMessageJson.put("connectionError", connectionErrorJson);
87+
eventStreamMessageJson.put("instanceId", instanceId);
7688

77-
FlutterPusherPlugin.eventSink.success(eventStreamMessageJson.toString());
89+
eventSink.success(eventStreamMessageJson.toString());
7890

79-
if (FlutterPusherPlugin.isLoggingEnabled) {
80-
Log.d(FlutterPusherPlugin.TAG, "onError : " + e.getMessage());
91+
if (isLoggingEnabled) {
92+
Log.d(TAG, "onError : " + e.getMessage());
8193
e.printStackTrace();
8294
}
8395
} catch (Exception ex) {
84-
if (FlutterPusherPlugin.isLoggingEnabled) {
85-
Log.d(FlutterPusherPlugin.TAG, "onError exception: " + e.getMessage());
96+
if (isLoggingEnabled) {
97+
Log.d(TAG, "onError exception: " + e.getMessage());
8698
ex.printStackTrace();
8799
}
88100
}

‎android/src/main/java/com/github/heywhy/flutter_pusher/listeners/PresenceChannelListener.java

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
public class PresenceChannelListener extends EventChannelListener implements PresenceChannelEventListener {
99

10+
public PresenceChannelListener(String instanceId, boolean isLoggingEnabled) {
11+
super(instanceId, isLoggingEnabled);
12+
}
13+
1014
@Override
1115
public void onSubscriptionSucceeded(String channelName) {
1216
this.onEvent(toPusherEvent(channelName, SUBSCRIPTION_SUCCESS_EVENT, null, null));

‎android/src/main/java/com/github/heywhy/flutter_pusher/listeners/PrivateChannelListener.java

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
public class PrivateChannelListener extends EventChannelListener implements PrivateChannelEventListener {
66

7+
public PrivateChannelListener(String instanceId, boolean isLoggingEnabled) {
8+
super(instanceId, isLoggingEnabled);
9+
}
10+
711
@Override
812
public void onSubscriptionSucceeded(String channelName) {
913
this.onEvent(toPusherEvent(channelName, SUBSCRIPTION_SUCCESS_EVENT, null, null));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.github.heywhy.flutter_pusher.platform_messages;
2+
3+
public class BindInstanceMessage extends InstanceMessage {
4+
private String eventName;
5+
private String channelName;
6+
7+
public String getEventName() {
8+
return eventName;
9+
}
10+
11+
public void setEventName(String eventName) {
12+
this.eventName = eventName;
13+
}
14+
15+
public String getChannelName() {
16+
return channelName;
17+
}
18+
19+
public void setChannelName(String channelName) {
20+
this.channelName = channelName;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.github.heywhy.flutter_pusher.platform_messages;
2+
3+
import java.util.Collections;
4+
import java.util.Map;
5+
6+
public class InitInstanceMessage extends InstanceMessage {
7+
8+
private String appKey;
9+
private InitOptions options;
10+
private boolean isLoggingEnabled = false;
11+
12+
public String getAppKey() {
13+
return appKey;
14+
}
15+
16+
public void setAppKey(String appKey) {
17+
this.appKey = appKey;
18+
}
19+
20+
public boolean getIsLoggingEnabled() {
21+
return isLoggingEnabled;
22+
}
23+
24+
public void setLoggingEnabled(boolean loggingEnabled) {
25+
isLoggingEnabled = loggingEnabled;
26+
}
27+
28+
public InitOptions getOptions() {
29+
return options;
30+
}
31+
32+
public void setOptions(InitOptions options) {
33+
this.options = options;
34+
}
35+
36+
public static class InitOptions {
37+
private int port;
38+
private String host;
39+
private String cluster;
40+
private AuthOption auth;
41+
private boolean encrypted = true;
42+
private int activityTimeout;
43+
44+
public int getPort() {
45+
return port;
46+
}
47+
48+
public void setPort(int port) {
49+
this.port = port;
50+
}
51+
52+
public String getHost() {
53+
return host;
54+
}
55+
56+
public void setHost(String host) {
57+
this.host = host;
58+
}
59+
60+
public String getCluster() {
61+
return cluster;
62+
}
63+
64+
public void setCluster(String cluster) {
65+
this.cluster = cluster;
66+
}
67+
68+
public AuthOption getAuth() {
69+
return auth;
70+
}
71+
72+
public void setAuth(AuthOption auth) {
73+
this.auth = auth;
74+
}
75+
76+
public boolean getEncrypted() {
77+
return encrypted;
78+
}
79+
80+
public void setEncrypted(boolean encrypted) {
81+
this.encrypted = encrypted;
82+
}
83+
84+
public int getActivityTimeout() {
85+
return activityTimeout;
86+
}
87+
88+
public void setActivityTimeout(int activityTimeout) {
89+
this.activityTimeout = activityTimeout;
90+
}
91+
}
92+
93+
public static class AuthOption {
94+
private String endpoint;
95+
private Map<String, String> headers = Collections.emptyMap();
96+
97+
public String getEndpoint() {
98+
return endpoint;
99+
}
100+
101+
public void setEndpoint(String endpoint) {
102+
this.endpoint = endpoint;
103+
}
104+
105+
public Map<String, String> getHeaders() {
106+
return headers;
107+
}
108+
109+
public void setHeaders(Map<String, String> headers) {
110+
this.headers = headers;
111+
}
112+
}
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.heywhy.flutter_pusher.platform_messages;
2+
3+
public class InstanceMessage {
4+
private String instanceId;
5+
6+
public String getInstanceId() {
7+
return instanceId;
8+
}
9+
10+
public void setInstanceId(String instanceId) {
11+
this.instanceId = instanceId;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.heywhy.flutter_pusher.platform_messages;
2+
3+
public class SubscriptionInstanceMessage extends InstanceMessage {
4+
5+
private String channelName;
6+
7+
public String getChannelName() {
8+
return channelName;
9+
}
10+
11+
public void setChannelName(String channelName) {
12+
this.channelName = channelName;
13+
}
14+
}

‎example/.flutter-plugins-dependencies

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_pusher_client","dependencies":[]}]}

‎example/lib/main.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class _MyAppState extends State<MyApp> {
3333

3434
pusher
3535
.subscribe('channel')
36-
.bind('event', (event) => log('event' + event.toJson().toString()));
36+
.bind('event', (event) => log('event =>' + event.toString()));
3737
} on PlatformException {
3838
platformVersion = 'Failed to get platform version.';
3939
}

‎example/pubspec.lock

+55-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
# Generated by pub
22
# See https://dart.dev/tools/pub/glossary#lockfile
33
packages:
4+
archive:
5+
dependency: transitive
6+
description:
7+
name: archive
8+
url: "https://pub.dartlang.org"
9+
source: hosted
10+
version: "2.0.11"
11+
args:
12+
dependency: transitive
13+
description:
14+
name: args
15+
url: "https://pub.dartlang.org"
16+
source: hosted
17+
version: "1.5.2"
418
async:
519
dependency: transitive
620
description:
721
name: async
822
url: "https://pub.dartlang.org"
923
source: hosted
10-
version: "2.3.0"
24+
version: "2.4.0"
1125
boolean_selector:
1226
dependency: transitive
1327
description:
@@ -29,6 +43,20 @@ packages:
2943
url: "https://pub.dartlang.org"
3044
source: hosted
3145
version: "1.14.11"
46+
convert:
47+
dependency: transitive
48+
description:
49+
name: convert
50+
url: "https://pub.dartlang.org"
51+
source: hosted
52+
version: "2.1.1"
53+
crypto:
54+
dependency: transitive
55+
description:
56+
name: crypto
57+
url: "https://pub.dartlang.org"
58+
source: hosted
59+
version: "2.1.3"
3260
cupertino_icons:
3361
dependency: "direct main"
3462
description:
@@ -47,12 +75,19 @@ packages:
4775
path: ".."
4876
relative: true
4977
source: path
50-
version: "0.1.1"
78+
version: "0.1.2"
5179
flutter_test:
5280
dependency: "direct dev"
5381
description: flutter
5482
source: sdk
5583
version: "0.0.0"
84+
image:
85+
dependency: transitive
86+
description:
87+
name: image
88+
url: "https://pub.dartlang.org"
89+
source: hosted
90+
version: "2.1.4"
5691
json_annotation:
5792
dependency: transitive
5893
description:
@@ -66,14 +101,14 @@ packages:
66101
name: matcher
67102
url: "https://pub.dartlang.org"
68103
source: hosted
69-
version: "0.12.5"
104+
version: "0.12.6"
70105
meta:
71106
dependency: transitive
72107
description:
73108
name: meta
74109
url: "https://pub.dartlang.org"
75110
source: hosted
76-
version: "1.1.7"
111+
version: "1.1.8"
77112
path:
78113
dependency: transitive
79114
description:
@@ -88,6 +123,13 @@ packages:
88123
url: "https://pub.dartlang.org"
89124
source: hosted
90125
version: "1.8.0+1"
126+
petitparser:
127+
dependency: transitive
128+
description:
129+
name: petitparser
130+
url: "https://pub.dartlang.org"
131+
source: hosted
132+
version: "2.4.0"
91133
quiver:
92134
dependency: transitive
93135
description:
@@ -141,7 +183,7 @@ packages:
141183
name: test_api
142184
url: "https://pub.dartlang.org"
143185
source: hosted
144-
version: "0.2.5"
186+
version: "0.2.11"
145187
typed_data:
146188
dependency: transitive
147189
description:
@@ -156,5 +198,12 @@ packages:
156198
url: "https://pub.dartlang.org"
157199
source: hosted
158200
version: "2.0.8"
201+
xml:
202+
dependency: transitive
203+
description:
204+
name: xml
205+
url: "https://pub.dartlang.org"
206+
source: hosted
207+
version: "3.5.0"
159208
sdks:
160-
dart: ">=2.3.0 <3.0.0"
209+
dart: ">=2.4.0 <3.0.0"
+359-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,364 @@
11
import Flutter
22
import UIKit
3+
import PusherSwift
34

4-
public class SwiftFlutterPusherPlugin: NSObject, FlutterPlugin {
5-
public static func register(with registrar: FlutterPluginRegistrar) {
6-
let channel = FlutterMethodChannel(name: "com.github.heywhy/pusher", binaryMessenger: registrar.messenger())
7-
let instance = SwiftFlutterPusherPlugin()
8-
registrar.addMethodCallDelegate(instance, channel: channel)
9-
}
5+
public class SwiftFlutterPusherPlugin: NSObject, FlutterPlugin, PusherDelegate {
106

11-
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12-
result("iOS " + UIDevice.current.systemVersion)
13-
}
7+
8+
public static var pusher: Pusher?
9+
public static var isLoggingEnabled: Bool = false;
10+
public static var bindedEvents = [String:String]()
11+
public static var channels = [String:PusherChannel]()
12+
public static var eventSink: FlutterEventSink?
13+
14+
public static func register(with registrar: FlutterPluginRegistrar) {
15+
let channel = FlutterMethodChannel(name: "com.github.heywhy/pusher", binaryMessenger: registrar.messenger())
16+
let instance = SwiftFlutterPusherPlugin()
17+
let eventChannel = FlutterEventChannel(name: "com.github.heywhy/pusherStream", binaryMessenger: registrar.messenger())
18+
19+
registrar.addMethodCallDelegate(instance, channel: channel)
20+
eventChannel.setStreamHandler(StreamHandler())
21+
}
22+
23+
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
24+
switch call.method {
25+
case "init":
26+
setup(call, result: result)
27+
case "connect":
28+
connect(call, result: result)
29+
case "disconnect":
30+
disconnect(call, result: result)
31+
case "subscribe":
32+
subscribe(call, result: result)
33+
case "unsubscribe":
34+
unsubscribe(call, result: result)
35+
case "bind":
36+
bind(call, result: result)
37+
case "unbind":
38+
unbind(call, result: result)
39+
case "trigger":
40+
// trigger(call, result: result)
41+
default:
42+
result(FlutterMethodNotImplemented)
43+
}
44+
}
45+
46+
47+
public func setup(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
48+
if let pusherObj = SwiftPusherPlugin.pusher {
49+
pusherObj.unbindAll();
50+
pusherObj.unsubscribeAll()
51+
}
52+
53+
for (_, pusherChannel) in SwiftPusherPlugin.channels {
54+
pusherChannel.unbindAll()
55+
}
56+
57+
SwiftPusherPlugin.channels.removeAll();
58+
SwiftPusherPlugin.bindedEvents.removeAll()
59+
60+
do {
61+
let json = call.arguments as! String
62+
let jsonDecoder = JSONDecoder()
63+
let initArgs = try jsonDecoder.decode(InitArgs.self, from: json.data(using: .utf8)!)
64+
65+
SwiftPusherPlugin.isLoggingEnabled = initArgs.isLoggingEnabled
66+
67+
let options = PusherClientOptions(
68+
authMethod: initArgs.options.auth != nil ? AuthMethod.authRequestBuilder(authRequestBuilder: AuthRequestBuilder(endpoint: initArgs.options.auth!.endpoint, headers: initArgs.options.auth!.headers)): .noMethod,
69+
host: initArgs.options.host != nil ? .host(initArgs.options.host!) : (initArgs.options.cluster != nil ? .cluster(initArgs.options.cluster!) : .host("ws.pusherapp.com")),
70+
port: initArgs.options.port ?? (initArgs.options.encrypted ?? true ? 443 : 80),
71+
encrypted: initArgs.options.encrypted ?? true,
72+
activityTimeout: Double(initArgs.options.activityTimeout ?? 30000) / 1000
73+
)
74+
75+
SwiftPusherPlugin.pusher = Pusher(
76+
key: initArgs.appKey,
77+
options: options
78+
)
79+
SwiftPusherPlugin.pusher!.connection.delegate = self
80+
81+
if (SwiftPusherPlugin.isLoggingEnabled) {
82+
print("Pusher init")
83+
}
84+
} catch {
85+
if (SwiftPusherPlugin.isLoggingEnabled) {
86+
print("Pusher init error:" + error.localizedDescription)
87+
}
88+
}
89+
result(nil);
90+
}
91+
92+
public func connect(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
93+
if let pusherObj = SwiftPusherPlugin.pusher {
94+
pusherObj.connect();
95+
if (SwiftPusherPlugin.isLoggingEnabled) {
96+
print("Pusher connect")
97+
}
98+
}
99+
result(nil);
100+
}
101+
102+
public func disconnect(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
103+
if let pusherObj = SwiftPusherPlugin.pusher {
104+
pusherObj.disconnect();
105+
if (SwiftPusherPlugin.isLoggingEnabled) {
106+
print("Pusher disconnect")
107+
}
108+
}
109+
result(nil);
110+
}
111+
112+
public func subscribe(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
113+
if let pusherObj = SwiftPusherPlugin.pusher {
114+
let channelName = call.arguments as! String
115+
let channelType = channelName.components(separatedBy: "-")[0]
116+
var channel: PusherChannel
117+
118+
switch channelType{
119+
case "private":
120+
channel = pusherObj.subscribe(channelName)
121+
if (SwiftPusherPlugin.isLoggingEnabled) {
122+
print("Pusher subscribe (private)")
123+
}
124+
case "presence":
125+
channel = pusherObj.subscribeToPresenceChannel(channelName: channelName)
126+
if (SwiftPusherPlugin.isLoggingEnabled) {
127+
print("Pusher subscribe (presence)")
128+
}
129+
default:
130+
channel = pusherObj.subscribe(channelName)
131+
if (SwiftPusherPlugin.isLoggingEnabled) {
132+
print("Pusher subscribe")
133+
}
134+
}
135+
136+
SwiftPusherPlugin.channels[channelName] = channel;
137+
}
138+
result(nil);
139+
}
140+
141+
public func subscribeToPresence(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
142+
if let pusherObj = SwiftPusherPlugin.pusher {
143+
let channelName = call.arguments as! String
144+
let channel = pusherObj.subscribeToPresenceChannel(channelName: channelName)
145+
SwiftPusherPlugin.channels[channelName] = channel;
146+
147+
if (SwiftPusherPlugin.isLoggingEnabled) {
148+
print("Pusher subscribe to presence channel")
149+
}
150+
}
151+
result(nil);
152+
}
153+
154+
public func unsubscribe(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
155+
if let pusherObj = SwiftPusherPlugin.pusher {
156+
let channelName = call.arguments as! String
157+
pusherObj.unsubscribe(channelName)
158+
SwiftPusherPlugin.channels.removeValue(forKey: "channelName")
159+
160+
if (SwiftPusherPlugin.isLoggingEnabled) {
161+
print("Pusher unsubscribe")
162+
}
163+
}
164+
result(nil);
165+
}
166+
167+
public func bind(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
168+
do {
169+
let json = call.arguments as! String
170+
let jsonDecoder = JSONDecoder()
171+
let bindArgs = try jsonDecoder.decode(BindArgs.self, from: json.data(using: .utf8)!)
172+
173+
let channel = SwiftPusherPlugin.channels[bindArgs.channelName]
174+
if let channelObj = channel {
175+
unbindIfBound(channelName: bindArgs.channelName, eventName: bindArgs.eventName)
176+
SwiftPusherPlugin.bindedEvents[bindArgs.channelName + bindArgs.eventName] = channelObj.bind(eventName: bindArgs.eventName, callback: { data in
177+
do {
178+
if let dataObj = data as? [String : AnyObject] {
179+
let pushJsonData = try! JSONSerialization.data(withJSONObject: dataObj)
180+
let pushJsonString = NSString(data: pushJsonData, encoding: String.Encoding.utf8.rawValue)
181+
let event = Event(channel: bindArgs.channelName, event: bindArgs.eventName, data: pushJsonString! as String)
182+
let message = PusherEventStreamMessage(event: event, connectionStateChange: nil)
183+
let jsonEncoder = JSONEncoder()
184+
let jsonData = try jsonEncoder.encode(message)
185+
let jsonString = String(data: jsonData, encoding: .utf8)
186+
if let eventSinkObj = SwiftPusherPlugin.eventSink {
187+
eventSinkObj(jsonString)
188+
189+
if (SwiftPusherPlugin.isLoggingEnabled) {
190+
print("Pusher event: CHANNEL:\(bindArgs.channelName) EVENT:\(bindArgs.eventName) DATA:\(jsonString ?? "no data")")
191+
}
192+
}
193+
}
194+
} catch {
195+
if (SwiftPusherPlugin.isLoggingEnabled) {
196+
print("Pusher bind error:" + error.localizedDescription)
197+
}
198+
}
199+
})
200+
if (SwiftPusherPlugin.isLoggingEnabled) {
201+
print("Pusher bind (\(bindArgs.eventName))")
202+
}
203+
}
204+
} catch {
205+
if (SwiftPusherPlugin.isLoggingEnabled) {
206+
print("Pusher bind error:" + error.localizedDescription)
207+
}
208+
}
209+
result(nil);
210+
}
211+
212+
public func unbind(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
213+
do {
214+
let json = call.arguments as! String
215+
let jsonDecoder = JSONDecoder()
216+
let bindArgs = try jsonDecoder.decode(BindArgs.self, from: json.data(using: .utf8)!)
217+
unbindIfBound(channelName: bindArgs.channelName, eventName: bindArgs.eventName)
218+
} catch {
219+
if (SwiftPusherPlugin.isLoggingEnabled) {
220+
print("Pusher unbind error:" + error.localizedDescription)
221+
}
222+
}
223+
result(nil);
224+
}
225+
226+
private func unbindIfBound(channelName: String, eventName: String) {
227+
let channel = SwiftPusherPlugin.channels[channelName]
228+
if let channelObj = channel {
229+
let callbackId = SwiftPusherPlugin.bindedEvents[channelName + eventName]
230+
if let callbackIdObj = callbackId {
231+
channelObj.unbind(eventName: eventName, callbackId: callbackIdObj)
232+
SwiftPusherPlugin.bindedEvents.removeValue(forKey: channelName + eventName)
233+
234+
if (SwiftPusherPlugin.isLoggingEnabled) {
235+
print("Pusher unbind")
236+
}
237+
}
238+
}
239+
}
240+
241+
public func changedConnectionState(from old: ConnectionState, to new: ConnectionState) {
242+
do {
243+
let stateChange = ConnectionStateChange(currentState: new.stringValue(), previousState: old.stringValue())
244+
let message = PusherEventStreamMessage(event: nil, connectionStateChange: stateChange)
245+
let jsonEncoder = JSONEncoder()
246+
let jsonData = try jsonEncoder.encode(message)
247+
let jsonString = String(data: jsonData, encoding: .utf8)
248+
if let eventSinkObj = SwiftPusherPlugin.eventSink {
249+
eventSinkObj(jsonString)
250+
}
251+
} catch {
252+
if (SwiftPusherPlugin.isLoggingEnabled) {
253+
print("Pusher changedConnectionState error:" + error.localizedDescription)
254+
}
255+
}
256+
}
257+
258+
public func trigger(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
259+
do {
260+
let json = call.arguments as! String
261+
let jsonDecoder = JSONDecoder()
262+
let bindArgs = try jsonDecoder.decode(BindArgs.self, from: json.data(using: .utf8)!)
263+
264+
let channel = SwiftPusherPlugin.channels[bindArgs.channelName]
265+
if let channelObj = channel {
266+
let eventName = bindArgs.eventName
267+
268+
channelObj.trigger(eventName: eventName, data: [])
269+
}
270+
} catch {
271+
if (SwiftPusherPlugin.isLoggingEnabled) {
272+
print("Pusher trigger error:" + error.localizedDescription)
273+
}
274+
}
275+
}
276+
}
277+
278+
279+
class AuthRequestBuilder: AuthRequestBuilderProtocol {
280+
var endpoint: String
281+
var headers: [String: String]
282+
283+
init(endpoint: String, headers: [String: String]) {
284+
self.endpoint = endpoint
285+
self.headers = headers
286+
}
287+
288+
func requestFor(socketID: String, channelName: String) -> URLRequest? {
289+
do{
290+
var request = URLRequest(url: URL(string: endpoint)!)
291+
request.httpMethod = "POST"
292+
293+
if(headers.values.contains("application/json")){
294+
let jsonEncoder = JSONEncoder()
295+
request.httpBody = try jsonEncoder.encode(["socket_id": socketID, "channel_name": channelName])
296+
}else{
297+
request.httpBody = "socket_id=\(socketID)&channel_name=\(channelName)".data(using: String.Encoding.utf8)
298+
}
299+
300+
for (key, value) in headers {
301+
request.addValue(value, forHTTPHeaderField: key)
302+
}
303+
return request
304+
} catch {
305+
if (SwiftPusherPlugin.isLoggingEnabled) {
306+
print("Authentication error:" + error.localizedDescription)
307+
}
308+
return nil
309+
}
310+
311+
}
312+
}
313+
314+
class StreamHandler: NSObject, FlutterStreamHandler {
315+
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
316+
SwiftPusherPlugin.eventSink = events
317+
return nil;
318+
}
319+
320+
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
321+
return nil;
322+
}
323+
}
324+
325+
struct InitArgs: Codable {
326+
var appKey: String
327+
var options: Options
328+
var isLoggingEnabled: Bool
329+
}
330+
331+
struct Options: Codable {
332+
var cluster: String?
333+
var host: String?
334+
var port: Int?
335+
var encrypted: Bool?
336+
var auth: Auth?
337+
var activityTimeout: Int?
338+
}
339+
340+
struct Auth: Codable{
341+
var endpoint: String
342+
var headers: [String: String]
343+
}
344+
345+
struct PusherEventStreamMessage: Codable {
346+
var event: Event?
347+
var connectionStateChange: ConnectionStateChange?
348+
}
349+
350+
struct ConnectionStateChange: Codable {
351+
var currentState: String
352+
var previousState: String
353+
}
354+
355+
struct Event: Codable {
356+
var channel: String
357+
var event: String
358+
var data: String
359+
}
360+
361+
struct BindArgs: Codable {
362+
var channelName: String
363+
var eventName: String
14364
}

‎ios/Classes/UserAgent.h

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Generated file, do not edit
2+
#define LIBRARY_VERSION @"0.0.4"
3+
#define LIBRARY_NAME @"flutter_pusher_client"
4+
#define PUSHER_LIBRARY_VERSION @"7.0.0"

‎ios/flutter_pusher.podspec

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#
22
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
33
#
4+
require 'yaml'
5+
pubspec = YAML.load_file(File.join('..', 'pubspec.yaml'))
6+
libraryVersion = pubspec['version'].gsub('+', '-')
7+
pusherLibraryVersion = '~> 7.2'
8+
49
Pod::Spec.new do |s|
510
s.name = 'flutter_pusher_client'
611
s.version = '0.1.0'
@@ -15,7 +20,15 @@ A new flutter plugin project.
1520
s.source_files = 'Classes/**/*'
1621
s.public_header_files = 'Classes/**/*.h'
1722
s.dependency 'Flutter'
23+
s.dependency 'PusherSwift', pusherLibraryVersion
24+
25+
s.ios.deployment_target = '9.0'
1826

19-
s.ios.deployment_target = '8.0'
27+
s.prepare_command = <<-CMD
28+
echo // Generated file, do not edit > Classes/UserAgent.h
29+
echo "#define LIBRARY_VERSION @\\"#{libraryVersion}\\"" >> Classes/UserAgent.h
30+
echo "#define LIBRARY_NAME @\\"flutter_pusher_client\\"" >> Classes/UserAgent.h
31+
echo "#define PUSHER_LIBRARY_VERSION @\\"#{pusherLibraryVersion}\\"" >> Classes/UserAgent.h
32+
CMD
2033
end
2134

‎lib/flutter_pusher.dart

+59-34
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ class Channel {
2626
}
2727

2828
void _subscribe() async {
29-
await _channel.invokeMethod('subscribe', this.name);
29+
await _channel.invokeMethod('subscribe',
30+
{'channelName': this.name, 'instanceId': pusher._instanceId});
3031
}
3132

3233
/// Bind to listen for events sent on the given channel
33-
Future bind(String eventName, void Function(Event) onEvent) async {
34+
Future bind(String eventName, Function onEvent) async {
3435
await this.pusher._bind(name, eventName, onEvent: onEvent);
3536
}
3637

@@ -54,14 +55,15 @@ class Channel {
5455
class FlutterPusher {
5556
static const MethodChannel _channel =
5657
const MethodChannel('com.github.heywhy/pusher');
57-
static const EventChannel _eventChannel =
58+
final EventChannel _eventChannel =
5859
const EventChannel('com.github.heywhy/pusherStream');
60+
static num _instances = 0;
5961

60-
Map<String, void Function(Event)> eventCallbacks =
61-
Map<String, void Function(Event)>();
62-
63-
static void Function(ConnectionError) _onError;
64-
static void Function(ConnectionStateChange) _onConnectionStateChange;
62+
num _instanceId;
63+
String _socketId;
64+
Map<String, Function> _eventCallbacks = Map<String, Function>();
65+
void Function(ConnectionError) _onError;
66+
void Function(ConnectionStateChange) _onConnectionStateChange;
6567

6668
FlutterPusher(
6769
String appKey,
@@ -72,12 +74,13 @@ class FlutterPusher {
7274
void Function(ConnectionStateChange) onConnectionStateChange,
7375
}) : assert(appKey != null),
7476
assert(options != null) {
75-
77+
_instanceId = _instances++;
7678
_onError = onError;
7779
_onConnectionStateChange = onConnectionStateChange;
7880
_init(appKey, options, enableLogging: enableLogging);
7981
if (!lazyConnect) {
80-
connect(onError: onError, onConnectionStateChange: onConnectionStateChange);
82+
connect(
83+
onError: onError, onConnectionStateChange: onConnectionStateChange);
8184
}
8285
}
8386

@@ -87,16 +90,18 @@ class FlutterPusher {
8790
void Function(ConnectionError) onError,
8891
}) async {
8992
_onConnectionStateChange = onConnectionStateChange != null
90-
? onConnectionStateChange : _onConnectionStateChange;
93+
? onConnectionStateChange
94+
: _onConnectionStateChange;
9195
_onError = onError != null ? onError : _onError;
9296

93-
await _channel.invokeMethod('connect');
97+
await _channel.invokeMethod(
98+
'connect', jsonEncode({'instanceId': _instanceId}));
9499
}
95100

96-
97101
/// Disconnect the client from pusher
98102
Future disconnect() async {
99-
await _channel.invokeMethod('disconnect');
103+
await _channel.invokeMethod(
104+
'disconnect', jsonEncode({'instanceId': _instanceId}));
100105
}
101106

102107
/// Subscribe to a channel
@@ -107,13 +112,19 @@ class FlutterPusher {
107112

108113
/// Unsubscribe from a channel
109114
Future unsubscribe(String channelName) async {
110-
await _channel.invokeMethod('unsubscribe', channelName);
115+
await _channel.invokeMethod(
116+
'unsubscribe', {'channelName': channelName, 'instanceId': _instanceId});
117+
}
118+
119+
String getSocketId() {
120+
return _socketId;
111121
}
112122

113123
void _init(String appKey, PusherOptions options, {bool enableLogging}) async {
114124
_eventChannel.receiveBroadcastStream().listen(_handleEvent);
115125

116126
final initArgs = jsonEncode(InitArgs(
127+
_instanceId,
117128
appKey,
118129
options,
119130
isLoggingEnabled: enableLogging,
@@ -122,16 +133,21 @@ class FlutterPusher {
122133
await _channel.invokeMethod('init', initArgs);
123134
}
124135

125-
void _handleEvent([dynamic arguments]) {
136+
void _handleEvent([dynamic arguments]) async {
126137
var message = PusherEventStreamMessage.fromJson(jsonDecode(arguments));
127138

139+
if (message.instanceId != _instanceId.toString()) {
140+
return;
141+
}
142+
128143
if (message.isEvent) {
129144
var callback =
130-
eventCallbacks[message.event.channel + message.event.event];
145+
_eventCallbacks[message.event.channel + message.event.event];
131146
if (callback != null) {
132-
callback(message.event);
147+
callback(jsonDecode(message.event.data));
133148
}
134149
} else if (message.isConnectionStateChange) {
150+
_socketId = await _channel.invokeMethod('getSocketId', {'instanceId': _instanceId});
135151
if (_onConnectionStateChange != null) {
136152
_onConnectionStateChange(message.connectionStateChange);
137153
}
@@ -145,29 +161,32 @@ class FlutterPusher {
145161
Future _bind(
146162
String channelName,
147163
String eventName, {
148-
void Function(Event) onEvent,
164+
Function onEvent,
149165
}) async {
150166
final bindArgs = jsonEncode(BindArgs(
167+
instanceId: _instanceId,
151168
channelName: channelName,
152169
eventName: eventName,
153170
).toJson());
154171

155-
eventCallbacks[channelName + eventName] = onEvent;
172+
_eventCallbacks[channelName + eventName] = onEvent;
156173
await _channel.invokeMethod('bind', bindArgs);
157174
}
158175

159176
Future _unbind(String channelName, String eventName) async {
160177
final bindArgs = jsonEncode(BindArgs(
178+
instanceId: _instanceId,
161179
channelName: channelName,
162180
eventName: eventName,
163181
).toJson());
164182

165-
eventCallbacks.remove(channelName + eventName);
183+
_eventCallbacks.remove(channelName + eventName);
166184
await _channel.invokeMethod('unbind', bindArgs);
167185
}
168186

169187
Future _trigger(String channelName, String eventName) async {
170188
final bindArgs = jsonEncode(BindArgs(
189+
instanceId: _instanceId,
171190
channelName: channelName,
172191
eventName: eventName,
173192
).toJson());
@@ -177,45 +196,47 @@ class FlutterPusher {
177196
}
178197

179198
class PusherClient extends FlutterPusher {
180-
PusherClient(
199+
PusherClient(
181200
String appKey,
182201
PusherOptions options, {
183202
bool lazyConnect = false,
184203
bool enableLogging = false,
185204
void Function(ConnectionError) onError,
186205
void Function(ConnectionStateChange) onConnectionStateChange,
187206
}) : super(
188-
appKey,
189-
options,
190-
onError: onError,
191-
lazyConnect: lazyConnect,
192-
enableLogging: enableLogging,
193-
onConnectionStateChange: onConnectionStateChange,
194-
);
207+
appKey,
208+
options,
209+
onError: onError,
210+
lazyConnect: lazyConnect,
211+
enableLogging: enableLogging,
212+
onConnectionStateChange: onConnectionStateChange,
213+
);
195214
}
196215

197-
198216
@JsonSerializable()
199217
class BindArgs {
218+
final int instanceId;
200219
final String channelName;
201220
final String eventName;
202221

203-
BindArgs({this.channelName, this.eventName});
222+
BindArgs({this.channelName, this.eventName, this.instanceId})
223+
: assert(instanceId != null);
204224

205225
factory BindArgs.fromJson(Map<String, dynamic> json) =>
206226
_$BindArgsFromJson(json);
207227

208228
Map<String, dynamic> toJson() => _$BindArgsToJson(this);
209229
}
210230

211-
212231
@JsonSerializable()
213232
class InitArgs {
233+
final int instanceId;
214234
final String appKey;
215235
final PusherOptions options;
216236
final bool isLoggingEnabled;
217237

218-
InitArgs(this.appKey, this.options, {this.isLoggingEnabled = false});
238+
InitArgs(this.instanceId, this.appKey, this.options,
239+
{this.isLoggingEnabled = false});
219240

220241
factory InitArgs.fromJson(Map<String, dynamic> json) =>
221242
_$InitArgsFromJson(json);
@@ -266,6 +287,7 @@ class PusherAuth {
266287
@JsonSerializable()
267288
class PusherEventStreamMessage {
268289
final Event event;
290+
final String instanceId;
269291
final ConnectionStateChange connectionStateChange;
270292
final ConnectionError connectionError;
271293

@@ -276,7 +298,10 @@ class PusherEventStreamMessage {
276298
bool get isConnectionError => connectionError != null;
277299

278300
PusherEventStreamMessage(
279-
{this.event, this.connectionStateChange, this.connectionError});
301+
{this.event,
302+
this.instanceId,
303+
this.connectionStateChange,
304+
this.connectionError});
280305

281306
factory PusherEventStreamMessage.fromJson(Map<String, dynamic> json) =>
282307
_$PusherEventStreamMessageFromJson(json);

‎lib/flutter_pusher.g.dart

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pubspec.lock

+33-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ packages:
1515
url: "https://pub.dartlang.org"
1616
source: hosted
1717
version: "0.2.1"
18+
archive:
19+
dependency: transitive
20+
description:
21+
name: archive
22+
url: "https://pub.dartlang.org"
23+
source: hosted
24+
version: "2.0.11"
1825
args:
1926
dependency: transitive
2027
description:
@@ -28,7 +35,7 @@ packages:
2835
name: async
2936
url: "https://pub.dartlang.org"
3037
source: hosted
31-
version: "2.3.0"
38+
version: "2.4.0"
3239
boolean_selector:
3340
dependency: transitive
3441
description:
@@ -221,6 +228,13 @@ packages:
221228
url: "https://pub.dartlang.org"
222229
source: hosted
223230
version: "3.1.3"
231+
image:
232+
dependency: transitive
233+
description:
234+
name: image
235+
url: "https://pub.dartlang.org"
236+
source: hosted
237+
version: "2.1.4"
224238
io:
225239
dependency: transitive
226240
description:
@@ -269,14 +283,14 @@ packages:
269283
name: matcher
270284
url: "https://pub.dartlang.org"
271285
source: hosted
272-
version: "0.12.5"
286+
version: "0.12.6"
273287
meta:
274288
dependency: transitive
275289
description:
276290
name: meta
277291
url: "https://pub.dartlang.org"
278292
source: hosted
279-
version: "1.1.7"
293+
version: "1.1.8"
280294
mime:
281295
dependency: transitive
282296
description:
@@ -326,6 +340,13 @@ packages:
326340
url: "https://pub.dartlang.org"
327341
source: hosted
328342
version: "1.8.0+1"
343+
petitparser:
344+
dependency: transitive
345+
description:
346+
name: petitparser
347+
url: "https://pub.dartlang.org"
348+
source: hosted
349+
version: "2.4.0"
329350
pool:
330351
dependency: transitive
331352
description:
@@ -428,7 +449,7 @@ packages:
428449
name: test_api
429450
url: "https://pub.dartlang.org"
430451
source: hosted
431-
version: "0.2.5"
452+
version: "0.2.11"
432453
timing:
433454
dependency: transitive
434455
description:
@@ -464,6 +485,13 @@ packages:
464485
url: "https://pub.dartlang.org"
465486
source: hosted
466487
version: "1.1.0"
488+
xml:
489+
dependency: transitive
490+
description:
491+
name: xml
492+
url: "https://pub.dartlang.org"
493+
source: hosted
494+
version: "3.5.0"
467495
yaml:
468496
dependency: transitive
469497
description:
@@ -472,4 +500,4 @@ packages:
472500
source: hosted
473501
version: "2.2.0"
474502
sdks:
475-
dart: ">=2.3.0 <3.0.0"
503+
dart: ">=2.4.0 <3.0.0"

‎pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: flutter_pusher_client
22
description: A Flutter plugin to listen to events sent through pusher. Wraps the native Java and Swift libraries
3-
version: 0.1.1
4-
author: Atanda Rasheed <atandarash@gmail.com>
3+
version: 0.1.2
4+
# author: Atanda Rasheed <atandarash@gmail.com>
55
homepage: https://github.com/heywhy/flutter-pusher-client
66

77
environment:

0 commit comments

Comments
 (0)
This repository has been archived.