Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pilot implementation for Realtime event based API's - ConnectionStateChange #6

Merged
merged 35 commits into from
Jun 20, 2020
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3455f8e
ObjectiveC: using const wherever required
Apr 30, 2020
6ed4eb0
[RTL6i] spec update: Rest.publish => name and/or data are optional
May 1, 2020
d453599
spec changes: assert based optional arguments. No more bases
May 1, 2020
3f99ee4
No more XXXBase - except AblyBase
May 1, 2020
e5c0baa
Passing Ably(plugin) instance to PlatformObjects
May 2, 2020
5350ef4
Passing Ably(plugin) instance to PlatformObjects
May 2, 2020
a1ddf71
realtime channels made final
May 2, 2020
e4765a0
pubspec.yaml update
May 2, 2020
db8150c
Adding StreamsChannel instance to ably_implementation
May 2, 2020
4202ebb
realtime implementation and other spec migrations
May 2, 2020
5dacd42
android part of StreamHandler - support for multiple listeners handled
May 2, 2020
d366382
dependency lock files
May 2, 2020
917ee29
working with codec pairs on Android platform side
May 3, 2020
a148fb6
[Android] cleanup, remove unwanted method handlers in method channel
May 3, 2020
5fdb4a9
update codec types, move ablyMessage to top as it is the base object …
May 3, 2020
44dbf65
[Android] remove MethodChannel based listeners
May 3, 2020
a98e3e6
[Flutter] remove MethodChannel based listeners
May 3, 2020
5a57946
eventName converted to PlatformMethod enum
May 3, 2020
a548171
[Android] update event names accordingly on platform side
May 3, 2020
3078030
[iOS] removing usages of `AblyFlutterSurfaceRealtime` and directly us…
May 3, 2020
b96f4e0
[iOS] Realtime Connection events handled
May 3, 2020
2a80530
example update
May 3, 2020
c8cee62
fixup LogLevel constants usage, and make the constant declarations as…
May 6, 2020
1b76052
reword `_realtimeStatus` to `_realtimeConnectionState`
May 6, 2020
31c9a6b
doc string for `AblyEventStreamHandler` and raise proper error on uni…
May 7, 2020
9ed9de3
staying consistent with spacing and docstrings vs regular comments
May 7, 2020
3027da0
Sanitize
May 7, 2020
82d2184
event names update at cocoa end
May 15, 2020
cb8090b
RTE6a
May 16, 2020
9f9a3f8
update README
May 16, 2020
083a0a5
format README.md source
tiholic May 27, 2020
d46b88e
Update README.md - no env suggestions
tiholic May 27, 2020
c777214
Update README.md
tiholic Jun 18, 2020
1d0f376
Update README
tiholic Jun 19, 2020
a6151d7
[housekeeping] Update README
tiholic Jun 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,92 @@ or visit our
[Support and Help](https://www.ably.io/support)
site to discuss privately.

## Running the example

* Clone the repo
* cd to `example` folder
* run `flutter packages get` to install dependencies
* `flutter run` will start the application on connected android / iOS device


## Using the Realtime API

##### Import the package

```dart
import 'package:ably_flutter_plugin/ably.dart' as ably;
```

##### Create an Ably instance

```dart
final ably.Ably ablyPlugin = ably.Ably();
```

##### create a ClientOptions

```dart
final clientOptions = ably.ClientOptions.fromKey("<YOUR APP KEY>");
clientOptions.environment = '<your env>'; //optional
clientOptions.logLevel = ably.LogLevel.verbose; //optional
```

##### Rest API

```dart
ably.Rest rest = await ablyPlugin.createRest(options: clientOptions);
```

Getting a channel instance

```dart
ably.RestChannel channel = rest.channels.get('test');
```

Publish rest messages

```dart
//passing both name and data
await channel.publish(name: "Hello", data: "Ably");
//passing just name
await channel.publish(name: "Hello");
//passing just data
await channel.publish(data: "Ably");
//publishing an empty message
await channel.publish();
```

##### Realtime API

```dart
ably.Realtime realtime = await ablyPlugin.createRealtime(options: clientOptions);
```

Listen to connection state change event

```dart
realtime.connection.on().listen((ably.ConnectionStateChange stateChange) async {
print('Realtime connection state changed: ${stateChange.event}');
setState(() { _realtimeConnectionState = stateChange.current; });
});
```

Listening to a particular event: `connected`

```dart
realtime.connection.on(ably.ConnectionEvent.connected).listen((ably.ConnectionStateChange stateChange) async {
print('Realtime connection state changed: ${stateChange.event}');
setState(() { _realtimeConnectionState = stateChange.current; });
});
```

Connect and disconnect to a realtime instance

```dart
realtime.connect(); //connect to realtime
realtime.disconnect(); //disconnect from realtime
```

## Contributing

We have some [Developer Notes](DeveloperNotes.md), but we're still learning too so they'll only help you so far, in fact there's probably a lot you can teach us!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package io.ably.flutter.plugin;

import android.os.Handler;
import android.os.Looper;

import io.ably.lib.realtime.ChannelStateListener;
import io.ably.lib.realtime.ConnectionStateListener;
import io.flutter.plugin.common.EventChannel;


/**
* Dart side can listen to Event Streams by pushing data to eventSink available in onListen method.
* Event listening can be cancelled when stream subscription is cancelled on dart side
*
* ref: https://api.flutter.dev/javadoc/io/flutter/plugin/common/EventChannel.StreamHandler.html
* */
public class AblyEventStreamHandler implements EventChannel.StreamHandler {
private final AblyMethodCallHandler methodCallHandler;

/**
* Constructor requiring methodCallHandler, as it is a singleton and has all instances stored
* Event listening can be started on an instance picked from the stored instances
* */
AblyEventStreamHandler(AblyMethodCallHandler methodCallHandler){
this.methodCallHandler = methodCallHandler;
}

/**
* Refer to the comments on AblyMethodCallHandler.MethodResultWrapper
* on why this customized EventSink is required
* */
private static class MainThreadEventSink implements EventChannel.EventSink {
private EventChannel.EventSink eventSink;
private Handler handler;

MainThreadEventSink(EventChannel.EventSink eventSink) {
this.eventSink = eventSink;
handler = new Handler(Looper.getMainLooper());
}

@Override
public void success(final Object o) {
handler.post(() -> eventSink.success(o)); //lambda for new Runnable
}

@Override
public void error(final String s, final String s1, final Object o) {
handler.post(() -> eventSink.error(s, s1, o));
}

@Override
public void endOfStream() {
//TODO work on this if required, or remove this TODO once all features are covered
}
}

// Listeners
private PluginConnectionStateListener connectionStateListener;
private PluginChannelStateListener channelStateListener;

private class Listener{
EventChannel.EventSink eventSink;
Listener(EventChannel.EventSink eventSink){ this.eventSink = eventSink; }
}

private class PluginConnectionStateListener extends Listener implements ConnectionStateListener {
PluginConnectionStateListener(EventChannel.EventSink eventSink){super(eventSink);}
public void onConnectionStateChanged(ConnectionStateChange stateChange){
eventSink.success(stateChange);
}
}

private class PluginChannelStateListener extends Listener implements ChannelStateListener {
PluginChannelStateListener(EventChannel.EventSink eventSink){super(eventSink);}
public void onChannelStateChanged(io.ably.lib.realtime.ChannelStateListener.ChannelStateChange stateChange){
eventSink.success(stateChange);
}
}

@Override
public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) {
MainThreadEventSink eventSink = new MainThreadEventSink(uiThreadEventSink);
methodCallHandler.<AblyFlutterMessage<String>>ablyDo((AblyFlutterMessage)object, (ablyLibrary, message) -> {
String eventName = message.message;
switch(eventName) {
case "realtime_onConnectionStateChanged":
connectionStateListener = new PluginConnectionStateListener(eventSink);
ablyLibrary.getRealtime(message.handle).connection.on(connectionStateListener);
return;
case "realtime_onChannelStateChanged":
// channelStateListener = new PluginChannelStateListener(eventSink);
// ablyLibrary.getRealtime(message.handle).connection.on(channelStateListener);
// return;
default:
eventSink.error("unhandled event", null, null);
}
});
}

@Override
public void onCancel(Object object) {
if(object==null){
System.out.println("Cannot process null input on cancel");
return;
}
methodCallHandler.<AblyFlutterMessage<String>>ablyDo((AblyFlutterMessage)object, (ablyLibrary, message) -> {
String eventName = message.message;
switch (eventName) {
case "realtime_onConnectionStateChanged":
ablyLibrary.getRealtime(message.handle).connection.off(connectionStateListener);
case "realtime_onChannelStateChanged":
// ablyLibrary.getRealtime(handle).connection.off(connectionStateListener);
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import androidx.annotation.NonNull;

import app.loup.streams_channel.StreamsChannel;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodCodec;
import io.flutter.plugin.common.PluginRegistry.Registrar;
Expand All @@ -15,8 +17,7 @@ private static MethodCodec createCodec() {

@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
final MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "ably_flutter_plugin", createCodec());
channel.setMethodCallHandler(AblyMethodCallHandler.getInstance());
setupChannels(flutterPluginBinding.getBinaryMessenger());
}

// This static function is optional and equivalent to onAttachedToEngine. It supports the old
Expand All @@ -29,8 +30,19 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
// depending on the user's project. onAttachedToEngine or registerWith must both be defined
// in the same class.
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "ably_flutter_plugin", createCodec());
channel.setMethodCallHandler(AblyMethodCallHandler.getInstance());
AblyFlutterPlugin.setupChannels(registrar.messenger());
}

private static void setupChannels(BinaryMessenger messenger){
MethodCodec codec = createCodec();
AblyMethodCallHandler methodCallHandler = AblyMethodCallHandler.getInstance();

final MethodChannel channel = new MethodChannel(messenger, "io.ably.flutter.plugin", codec);
channel.setMethodCallHandler(methodCallHandler);

final StreamsChannel streamsChannel = new StreamsChannel(messenger, "io.ably.flutter.stream", codec);
streamsChannel.setStreamHandlerFactory(arguments -> new AblyEventStreamHandler(methodCallHandler));

}

@Override
Expand Down
16 changes: 0 additions & 16 deletions android/src/main/java/io/ably/flutter/plugin/AblyLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class AblyLibrary {
// and as per this answer https://stackoverflow.com/a/31413003
private final LongSparseArray<AblyRest> _restInstances = new LongSparseArray<>();
private final LongSparseArray<AblyRealtime> _realtimeInstances = new LongSparseArray<>();
private final LongSparseArray<AblyRealtimeConnectionListener> _connectionListeners = new LongSparseArray<>();

private void assertNotDisposed() {
if (_disposed) {
Expand Down Expand Up @@ -51,21 +50,6 @@ AblyRest getRest(final long handle){
return _restInstances.get(handle);
}

long createConnectionListener(final AblyRealtime realtime) {
assertNotDisposed();

final AblyRealtimeConnectionListener listener = new AblyRealtimeConnectionListener();
realtime.connection.on(listener);
_connectionListeners.put(_nextHandle, listener);
return _nextHandle++;
}

AblyRealtimeConnectionListener getConnectionListener(final long handle) {
assertNotDisposed();

return _connectionListeners.get(handle);
}

void dispose() {
assertNotDisposed();

Expand Down
Loading