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

re-codec using JSON and Codegen #13

Merged
merged 19 commits into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions DeveloperNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@ being a specialized package that includes platform-specific implementation code
- Flutter
[documentation](https://flutter.dev/docs), offering tutorials,
samples, guidance on mobile development, and a full API reference.


## Generating platform constants

Some files in the project are generated to maintain sync between
platform constants on both native and dart side.
Generated file paths are configured as values in `bin/codegen.dart` for `toGenerate` Map

[Read about generation of platform specific constant files](bin/README.md)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.os.Handler;
import android.os.Looper;

import io.ably.flutter.plugin.generated.PlatformConstants;
import io.ably.lib.realtime.ChannelStateListener;
import io.ably.lib.realtime.ConnectionStateListener;
import io.flutter.plugin.common.EventChannel;
Expand Down Expand Up @@ -49,14 +50,11 @@ public void error(final String s, final String s1, final Object o) {
}

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

// Listeners
private PluginConnectionStateListener connectionStateListener;
private PluginChannelStateListener channelStateListener;

private class Listener{
EventChannel.EventSink eventSink;
Expand All @@ -70,27 +68,16 @@ public void onConnectionStateChanged(ConnectionStateChange 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":
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
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);
}
Expand All @@ -106,9 +93,9 @@ public void onCancel(Object object) {
methodCallHandler.<AblyFlutterMessage<String>>ablyDo((AblyFlutterMessage)object, (ablyLibrary, message) -> {
String eventName = message.message;
switch (eventName) {
case "realtime_onConnectionStateChanged":
case PlatformConstants.PlatformMethod.onRealtimeConnectionStateChanged:
ablyLibrary.getRealtime(message.handle).connection.off(connectionStateListener);
case "realtime_onChannelStateChanged":
case PlatformConstants.PlatformMethod.onRealtimeChannelStateChanged:
// ablyLibrary.getRealtime(handle).connection.off(connectionStateListener);
}
});
Expand Down
262 changes: 130 additions & 132 deletions android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import io.ably.lib.types.ErrorInfo;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.ably.flutter.plugin.generated.PlatformConstants;

public class AblyMethodCallHandler implements MethodChannel.MethodCallHandler {
private static AblyMethodCallHandler _instance;
Expand All @@ -35,18 +36,18 @@ static synchronized AblyMethodCallHandler getInstance() {

private AblyMethodCallHandler() {
_map = new HashMap<>();
_map.put("getPlatformVersion", this::getPlatformVersion);
_map.put("getVersion", this::getVersion);
_map.put("register", this::register);
_map.put(PlatformConstants.PlatformMethod.getPlatformVersion, this::getPlatformVersion);
_map.put(PlatformConstants.PlatformMethod.getVersion, this::getVersion);
_map.put(PlatformConstants.PlatformMethod.registerAbly, this::register);

// Rest
_map.put("createRestWithOptions", this::createRestWithOptions);
_map.put("publish", this::publishRestMessage);
_map.put(PlatformConstants.PlatformMethod.createRestWithOptions, this::createRestWithOptions);
_map.put(PlatformConstants.PlatformMethod.publish, this::publishRestMessage);

//Realtime
_map.put("createRealtimeWithOptions", this::createRealtimeWithOptions);
_map.put("connectRealtime", this::connectRealtime);
_map.put("closeRealtime", this::closeRealtime);
_map.put(PlatformConstants.PlatformMethod.createRealtimeWithOptions, this::createRealtimeWithOptions);
_map.put(PlatformConstants.PlatformMethod.connectRealtime, this::connectRealtime);
_map.put(PlatformConstants.PlatformMethod.closeRealtime, this::closeRealtime);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// Generated code. Do not modify.
// source file can be found at bin/templates'
//

package io.ably.flutter.plugin.generated;


final public class PlatformConstants{

final public class CodecTypes {
public static final byte ablyMessage = (byte)128;
public static final byte clientOptions = (byte)129;
public static final byte errorInfo = (byte)144;
public static final byte connectionStateChange = (byte)201;
public static final byte channelStateChange = (byte)202;
}

final public class PlatformMethod {
public static final String getPlatformVersion = "getPlatformVersion";
public static final String getVersion = "getVersion";
public static final String registerAbly = "registerAbly";
public static final String createRestWithOptions = "createRestWithOptions";
public static final String publish = "publish";
public static final String createRealtimeWithOptions = "createRealtimeWithOptions";
public static final String connectRealtime = "connectRealtime";
public static final String closeRealtime = "closeRealtime";
public static final String onRealtimeConnectionStateChanged = "onRealtimeConnectionStateChanged";
public static final String onRealtimeChannelStateChanged = "onRealtimeChannelStateChanged";
}

final public class TxAblyMessage{
public static final String registrationHandle = "registrationHandle";
public static final String type = "type";
public static final String message = "message";
}

final public class TxErrorInfo{
public static final String code = "code";
public static final String message = "message";
public static final String statusCode = "statusCode";
public static final String href = "href";
public static final String requestId = "requestId";
public static final String cause = "cause";
}

final public class TxClientOptions{
public static final String authUrl = "authUrl";
public static final String authMethod = "authMethod";
public static final String key = "key";
public static final String tokenDetails = "tokenDetails";
public static final String authHeaders = "authHeaders";
public static final String authParams = "authParams";
public static final String queryTime = "queryTime";
public static final String useTokenAuth = "useTokenAuth";
public static final String clientId = "clientId";
public static final String logLevel = "logLevel";
public static final String tls = "tls";
public static final String restHost = "restHost";
public static final String realtimeHost = "realtimeHost";
public static final String port = "port";
public static final String tlsPort = "tlsPort";
public static final String autoConnect = "autoConnect";
public static final String useBinaryProtocol = "useBinaryProtocol";
public static final String queueMessages = "queueMessages";
public static final String echoMessages = "echoMessages";
public static final String recover = "recover";
public static final String environment = "environment";
public static final String idempotentRestPublishing = "idempotentRestPublishing";
public static final String httpOpenTimeout = "httpOpenTimeout";
public static final String httpRequestTimeout = "httpRequestTimeout";
public static final String httpMaxRetryCount = "httpMaxRetryCount";
public static final String realtimeRequestTimeout = "realtimeRequestTimeout";
public static final String fallbackHosts = "fallbackHosts";
public static final String fallbackHostsUseDefault = "fallbackHostsUseDefault";
public static final String fallbackRetryTimeout = "fallbackRetryTimeout";
public static final String defaultTokenParams = "defaultTokenParams";
public static final String channelRetryTimeout = "channelRetryTimeout";
public static final String transportParams = "transportParams";
}

final public class TxTokenDetails{
public static final String token = "token";
public static final String expires = "expires";
public static final String issued = "issued";
public static final String capability = "capability";
public static final String clientId = "clientId";
}

final public class TxTokenParams{
public static final String capability = "capability";
public static final String clientId = "clientId";
public static final String nonce = "nonce";
public static final String timestamp = "timestamp";
public static final String ttl = "ttl";
}

final public class TxConnectionStateChange{
public static final String current = "current";
public static final String previous = "previous";
public static final String event = "event";
public static final String retryIn = "retryIn";
public static final String reason = "reason";
}

final public class TxChannelStateChange{
public static final String current = "current";
public static final String previous = "previous";
public static final String event = "event";
public static final String resumed = "resumed";
public static final String reason = "reason";
}

}
47 changes: 47 additions & 0 deletions bin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Code generation to keep platform constants in sync
QuintinWillison marked this conversation as resolved.
Show resolved Hide resolved

There are many platform constants that need to be sync on dart side and platform side.
Following are the constants that are being generated:
1. codec types
2. platform method and event names
3. serializable property names for serialization and de-serialization

#### Generating files

```bash
cd bin
dart codegen.dart
```

#### Template format

A straight forward templates creating using dart string interpolation:

#### Template and Context files

source template files are available in `bin/templates`
and source context data in `bin/codegencontext.dart`.


#### Generated files

These files are generated/modified upon code generation

1. `lib/src/generated/platformconstants.dart` for use in Flutter/Dart
2. `android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java` for use in Android/Java
3. `ios/Classes/codec/AblyPlatformConstants.h` for use in iOS/Objective-C
4. `ios/Classes/codec/AblyPlatformConstants.m` for use in iOS/Objective-C

#### When would I need to run code generation?

When any of the below need to be added/updated
1. A new codec type - required when a new top level serializable object is required (ex: `ErrorInfo` and `ClientOptions`)
2. Platform and event names - when implementing a new method in `MethodChannel` or new event in `EventChannel`
3. A new object needs to be serialized (either top-level, or nested)


#### What should I do after running code generation?

1. Test that everything still works
2. Commit changes to the template(s)
3. Commit changes to the generate files
34 changes: 34 additions & 0 deletions bin/codegen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:io';
tiholic marked this conversation as resolved.
Show resolved Hide resolved
import 'codegencontext.dart' show context;
import 'templates/platformconstants.dart.dart' as dart_template;
import 'templates/platformconstants.java.dart' as java_template;
import 'templates/platformconstants.h.dart' as objc_header_template;
import 'templates/platformconstants.m.dart' as objc_impl_template;


typedef String Template(Map<String, dynamic> context);

String projectRoot = "../";

Map<Template, String> toGenerate = {
// input template method vs output file path
dart_template.$: "${projectRoot}lib/src/generated/platformconstants.dart",
java_template.$: "${projectRoot}android/src/main/java/io/ably/flutter/plugin/generated/PlatformConstants.java",
objc_header_template.$: "${projectRoot}ios/Classes/codec/AblyPlatformConstants.h",
objc_impl_template.$: "${projectRoot}ios/Classes/codec/AblyPlatformConstants.m",
};

void main() async {
for(MapEntry<Template, String> entry in toGenerate.entries){
String source = entry.key(context);
File(entry.value).writeAsStringSync(
'''//
// Generated code. Do not modify.
// source file can be found at bin/templates'
//

${source}'''
);
print("File written: ${entry.value} ✔");
}
}
Loading