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

AuthCallback and InstanceStore Refactoring and documentation #250

Merged
merged 25 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9f916f8
Remove unnecessary storage of tokens `_restTokenData` and `_realtimeT…
ben-xD Nov 23, 2021
6044a75
Improve documentation on authCallback
ben-xD Nov 23, 2021
48363fe
Merge remote-tracking branch 'origin/main' into enhancement/simplify-…
ben-xD Nov 23, 2021
28f9c24
Instead of passing a callback (ResetAblyClientsCallback), i just pass…
ben-xD Nov 23, 2021
520fdb5
Move client creation out of AblyFlutter, to allow it to do 1 thing: s…
ben-xD Nov 23, 2021
0c75916
Merge branch 'fix/ios/remove-background-queue' into enhancement/simpl…
ben-xD Nov 23, 2021
154b645
Fix reset method to also reset paginatedResults, and improve naming
ben-xD Nov 23, 2021
d767faf
Move methodChannel out of AblyFlutter (the instance store) into AblyF…
ben-xD Nov 23, 2021
feb6e3b
Rename platform methods to `createRest` and `createRealtime`
ben-xD Nov 23, 2021
4ab0d77
Remove whitespace
ben-xD Nov 23, 2021
9519771
Rename AblyFlutterPlugin to AblyFlutter, since it the package was ren…
ben-xD Nov 23, 2021
13c3473
Fix parameter name
ben-xD Nov 23, 2021
4bfda03
Rename variable
ben-xD Nov 23, 2021
338ce8c
Temporarily rename AblyFlutter back AblyFlutterPlugin to help merge c…
ben-xD Nov 23, 2021
7cd1f9d
Merge branch 'fix/ios/remove-background-queue' into enhancement/simpl…
ben-xD Nov 23, 2021
9e4a4e2
Rename AblyFlutterPlugin.h to AblyFlutter.h
ben-xD Nov 23, 2021
f55c5f6
Remove redundant return statements
ben-xD Nov 23, 2021
4b1a922
Add whitespace around code block
ben-xD Nov 23, 2021
ddb0585
Fix symbols to be `AblyFlutter`
ben-xD Nov 23, 2021
5cac013
Replace synchronised with dispatch_once
ben-xD Nov 29, 2021
479e1c9
Add empty line at end of file
ben-xD Nov 30, 2021
345acab
Merge branch 'main' into enhancement/simplify-auth-callback-state
ben-xD Nov 30, 2021
2b86b3e
Merge remote-tracking branch 'origin/main' into enhancement/simplify-…
ben-xD Nov 30, 2021
1f1e81d
Remove unnecessary and unidiomatic use of underscore
ben-xD Dec 2, 2021
48a871a
Fix typo
ben-xD Dec 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 26 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,47 +131,48 @@ Authenticating using [token authentication](https://ably.com/documentation/core-
// Used to create a clientId when a client first doesn't have one.
// Note: you should implement `createTokenRequest`, which makes a request to your server that uses your Ably API key directly.
final clientOptions = ably.ClientOptions()
// ..clientId = _clientId // Optionally set the clientId
..autoConnect = false
// If a clientId was set in ClientOptions, it will be available in ably.TokenParams.
// If a clientId was set in ClientOptions, it will be available in the authCallback's first parameter (ably.TokenParams).
..clientId = _clientId
..authCallback = (ably.TokenParams tokenParams) async {
try {
// Send the tokenParamsMap (Map<String, dynamic>) to your server, using it to create a TokenRequest.
final Map<String, dynamic> tokenRequestMap = await createTokenRequest(
tokenParams.toMap());
return ably.TokenRequest.fromMap(tokenRequestMap);
} catch (e) {
print("Something went wrong in the authCallback: ${e}");
} on http.ClientException catch (e) {
print('Failed to createTokenRequest: $e');
rethrow;
}
};
this._ablyClient = new ably.Realtime(options: clientOptions);
await this._ablyClient.connect();
final realtime = ably.Realtime(options: clientOptions);
```

An example of `createTokenRequest`'s implementation making a network request to your server could be:

```dart
Future<Map<String, dynamic>> createTokenRequest(
Map<String, dynamic> tokenParamsMap) async {
var url = Uri.parse('https://example.com/api/v1/createTokenRequest');
// For debugging:
bool runningServerLocally = true;
if (runningServerLocally) {
if (Platform.isAndroid) { // Connect Android device to local server
url = Uri.parse(
'http://localhost:6001/api/v1/createTokenRequest');
} else if (Platform.isIOS) { // Connect iOS device to local server
const localDeviceIPAddress = '192.168.1.9';
url = Uri.parse(
'http://$localDeviceIPAddress:6001/api/v1/createTokenRequest');
}
Future<Map<String, dynamic>> createTokenRequest(
Map<String, dynamic> tokenParamsMap) async {
var url = Uri.parse('https://example.com/api/v1/createTokenRequest');
// For debugging:
bool runningServerLocally = true;
if (runningServerLocally) {
if (Platform.isAndroid) { // Connect Android device to local server
url = Uri.parse(
'http://localhost:6001/api/v1/createTokenRequest');
} else if (Platform.isIOS) { // Connect iOS device to local server
const localDeviceIPAddress = '192.168.1.9';
url = Uri.parse(
'http://$localDeviceIPAddress:6001/api/v1/createTokenRequest');
}

final response = await http.post(url, body: tokenParamsMap);
return jsonDecode(response.body) as Map<String, dynamic>;
}

final response = await http.post(url, body: tokenParamsMap);
return jsonDecode(response.body) as Map<String, dynamic>;
}
```
- To connect using `http` on iOS for debugging, you will need to explicitly enable this in your `Info.plist` file, by following [Transport security has blocked a cleartext HTTP](https://stackoverflow.com/a/30751597/7365866).

- **Android:** To connect to a local server running on a computer from an Android device, you can redirect the port on your device to the port the application is hosted on on your computer. For example, if you want to connect to a local server running at `localhost:3000` but connect from Android from `localhost:80` Run `adb reverse tcp:80 tcp:3000`.
- **iOS:** To connect to a local server running on a computer using `http` on iOS for debugging, you will need to explicitly enable this in your `Info.plist` file, by following [Transport security has blocked a cleartext HTTP](https://stackoverflow.com/a/30751597/7365866). To allow devices to connect from the IP address of your device, you need your server to listen on `0.0.0.0` instead of `127.0.0.1`.

### Using the REST API

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.ably.flutter.plugin;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
Expand All @@ -13,7 +12,6 @@
import io.ably.lib.realtime.ConnectionStateListener;
import io.ably.lib.realtime.Presence;
import io.ably.lib.types.AblyException;
import io.ably.lib.types.ChannelOptions;
import io.ably.lib.types.Message;
import io.ably.lib.types.PresenceMessage;
import io.flutter.plugin.common.EventChannel;
Expand All @@ -28,24 +26,15 @@
public class AblyEventStreamHandler implements EventChannel.StreamHandler {

private static final String TAG = AblyEventStreamHandler.class.getName();
/**
* Creating an ablyLibrary instance.
* As ablyLibrary is a singleton,
* all ably object instance will be accessible
*/
private final AblyLibrary ablyLibrary;

public AblyEventStreamHandler(Context applicationContext) {
ablyLibrary = AblyLibrary.getInstance(applicationContext);
}
private final AblyInstanceStore instanceStore = AblyInstanceStore.getInstance();

/**
* 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;
private final EventChannel.EventSink eventSink;
private final Handler handler;

MainThreadEventSink(EventChannel.EventSink eventSink) {
this.eventSink = eventSink;
Expand All @@ -69,10 +58,8 @@ public void endOfStream() {

// Listeners
private PluginConnectionStateListener connectionStateListener;

private PluginChannelStateListener channelStateListener;
private PluginChannelMessageListener channelMessageListener;

private PluginChannelPresenceMessageListener channelPresenceMessageListener;

void handleAblyException(EventChannel.EventSink eventSink, AblyException ablyException) {
Expand Down Expand Up @@ -135,28 +122,23 @@ public void onPresenceMessage(PresenceMessage message) {

}

// Casting stream creation arguments from `Object` into `AblyFlutterMessage<AblyEventMessage>`
private AblyFlutterMessage<AblyEventMessage<Object>> getMessage(Object message) {
return (AblyFlutterMessage<AblyEventMessage<Object>>) message;
}

@Override
public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) {
MainThreadEventSink eventSink = new MainThreadEventSink(uiThreadEventSink);
final AblyFlutterMessage<AblyEventMessage<Object>> ablyMessage = getMessage(object);
final AblyFlutterMessage<AblyEventMessage<Object>> ablyMessage = (AblyFlutterMessage<AblyEventMessage<Object>>) object;
final AblyEventMessage<Object> eventMessage = ablyMessage.message;
final String eventName = eventMessage.eventName;
final Map<String, Object> eventPayload = (eventMessage.message == null) ? null : (Map<String, Object>) eventMessage.message;
try {
switch (eventName) {
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
connectionStateListener = new PluginConnectionStateListener(eventSink);
ablyLibrary.getRealtime(ablyMessage.handle).connection.on(connectionStateListener);
instanceStore.getRealtime(ablyMessage.handle).connection.on(connectionStateListener);
break;
case PlatformConstants.PlatformMethod.onRealtimeChannelStateChanged:
assert eventPayload != null : "onRealtimeChannelStateChanged: event message is missing";
channelStateListener = new PluginChannelStateListener(eventSink);
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
Expand All @@ -166,7 +148,7 @@ public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) {
assert eventPayload != null : "onRealtimeChannelMessage: event message is missing";
try {
channelMessageListener = new PluginChannelMessageListener(eventSink);
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
Expand All @@ -179,7 +161,7 @@ public void onListen(Object object, EventChannel.EventSink uiThreadEventSink) {
assert eventPayload != null : "onRealtimePresenceMessage: event message is missing";
try {
channelPresenceMessageListener = new PluginChannelPresenceMessageListener(eventSink);
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
Expand All @@ -202,35 +184,35 @@ public void onCancel(Object object) {
Log.w(TAG, "onCancel cannot decode null");
return;
}
final AblyFlutterMessage<AblyEventMessage<Object>> ablyMessage = getMessage(object);
final AblyFlutterMessage<AblyEventMessage<Object>> ablyMessage = (AblyFlutterMessage<AblyEventMessage<Object>>) object;
final AblyEventMessage<Object> eventMessage = ablyMessage.message;
final String eventName = eventMessage.eventName;
final Map<String, Object> eventPayload = (eventMessage.message == null) ? null : (Map<String, Object>) eventMessage.message;
switch (eventName) {
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
ablyLibrary.getRealtime(ablyMessage.handle).connection.off(connectionStateListener);
instanceStore.getRealtime(ablyMessage.handle).connection.off(connectionStateListener);
break;
case PlatformConstants.PlatformMethod.onRealtimeChannelStateChanged:
// Note: this and all other assert statements in this onCancel method are
// left as is as there is no way of propagating this error to flutter side
assert eventPayload != null : "onRealtimeChannelStateChanged: event message is missing";
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
.off(channelStateListener);
break;
case PlatformConstants.PlatformMethod.onRealtimeChannelMessage:
assert eventPayload != null : "onRealtimeChannelMessage: event message is missing";
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
.unsubscribe(channelMessageListener);
break;
case PlatformConstants.PlatformMethod.onRealtimePresenceMessage:
assert eventPayload != null : "onRealtimePresenceMessage: event message is missing";
ablyLibrary
instanceStore
.getRealtime(ablyMessage.handle)
.channels
.get((String) eventPayload.get(PlatformConstants.TxTransportKeys.channelName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.common.StandardMethodCodec;

public class AblyFlutterPlugin implements FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener {
private static final String TAG = AblyFlutterPlugin.class.getName();
public class AblyFlutter implements FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener {
private static final String TAG = AblyFlutter.class.getName();
private Context applicationContext;
private AblyMethodCallHandler methodCallHandler;
private Activity mainActivity;
Expand All @@ -50,7 +50,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
// in the same class.
public static void registerWith(Registrar registrar) {
Context applicationContext = registrar.context().getApplicationContext();
final AblyFlutterPlugin ably = new AblyFlutterPlugin();
final AblyFlutter ably = new AblyFlutter();
registrar.addNewIntentListener(ably);
ably.setupChannels(registrar.messenger(), applicationContext);
}
Expand All @@ -59,15 +59,11 @@ private void setupChannels(BinaryMessenger messenger, Context applicationContext
final MethodCodec codec = createCodec(new CipherParamsStorage());

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

methodChannel = new MethodChannel(messenger, "io.ably.flutter.plugin", codec);
methodCallHandler = new AblyMethodCallHandler(
methodChannel,
streamsChannel::reset,
applicationContext
);
BackgroundMethodCallHandler backgroundMethodCallHandler = new BackgroundMethodCallHandler(messenger, codec);
methodCallHandler = new AblyMethodCallHandler(methodChannel, streamsChannel, applicationContext);
new BackgroundMethodCallHandler(messenger, codec);
methodChannel.setMethodCallHandler(methodCallHandler);
PushActivationEventHandlers.instantiate(applicationContext, methodChannel);
PushMessagingEventHandlers.reset(applicationContext, methodChannel);
Expand Down