Skip to content

Commit

Permalink
Merge pull request #16 from andre-paraense/all-flags-listener
Browse files Browse the repository at this point in the history
All flags listener
  • Loading branch information
andre-paraense authored Feb 11, 2020
2 parents 6548584 + e42eec3 commit 0208dc3
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.4.0

* Adding all flags listener support.

## 0.3.0

* Adding all flags support.
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'com.oakam.launchdarkly_flutter'
version '0.3.0'
version '0.4.0'

buildscript {
repositories {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.oakam.launchdarkly_flutter;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.launchdarkly.android.FeatureFlagChangeListener;
import com.launchdarkly.android.LDAllFlagsListener;
import com.launchdarkly.android.LDClient;
import com.launchdarkly.android.LDConfig;
import com.launchdarkly.android.LDUser;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
Expand All @@ -30,6 +35,7 @@ public class LaunchdarklyFlutterPlugin implements FlutterPlugin, ActivityAware,
private Activity activity;
private LDClient ldClient;
private Map<String, FeatureFlagChangeListener> listeners = new HashMap<>();
private Map<String, LDAllFlagsListener> allFlagsListeners = new HashMap<>();

public LaunchdarklyFlutterPlugin() {}

Expand Down Expand Up @@ -127,23 +133,31 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
result.success(ldClient.stringVariation(flagKey,fallback));
} else if (call.method.equals("allFlags")) {
result.success(ldClient.allFlags());
}else if (call.method.equals("registerFeatureFlagListener")) {
} else if (call.method.equals("registerFeatureFlagListener")) {

String flagKey = call.argument("flagKey");

FeatureFlagChangeListener listener = new FeatureFlagChangeListener() {
@Override
public void onFeatureFlagChange(String flagKey) {
Map<String, String> arguments = new HashMap<>();
arguments.put("flagKey",flagKey);

channel.invokeMethod("callbackRegisterFeatureFlagListener",arguments);
public void onFeatureFlagChange(final String flagKey) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Map<String, String> arguments = new HashMap<>();
arguments.put("flagKey",flagKey);
try{
channel.invokeMethod("callbackRegisterFeatureFlagListener",arguments);
}catch (Exception e){
Log.e("FeatureFlagsListener", e.getMessage());
}
}
});
}
};

ldClient.registerFeatureFlagListener(flagKey, listener);
listeners.put(flagKey, listener);

result.success(true);
} else if (call.method.equals("unregisterFeatureFlagListener")) {
String flagKey = call.argument("flagKey");
if (listeners.containsKey(flagKey)) {
Expand All @@ -153,7 +167,42 @@ public void onFeatureFlagChange(String flagKey) {
return;
}
result.success(false);
} else {
} else if (call.method.equals("registerAllFlagsListener")) {

String listenerId = call.argument("listenerId");

LDAllFlagsListener listener = new LDAllFlagsListener() {
@Override
public void onChange(final List<String> flagKeys) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Map<String, List<String>> arguments = new HashMap<>();
arguments.put("flagKeys",flagKeys);

try{
channel.invokeMethod("callbackAllFlagsListener",arguments);
}catch (Exception e){
Log.e("callAllFlagsListener", e.getMessage());
}
}
});
}
};

ldClient.registerAllFlagsListener(listener);
allFlagsListeners.put(listenerId, listener);
result.success(true);
} else if (call.method.equals("unregisterAllFlagsListener")) {
String listenerId = call.argument("listenerId");
if (allFlagsListeners.containsKey(listenerId)) {
ldClient.unregisterAllFlagsListener(allFlagsListeners.get(listenerId));
listeners.remove(listenerId);
result.success(true);
return;
}
result.success(false);
}else {
result.notImplemented();
}
}
Expand Down
36 changes: 34 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class _MyAppState extends State<MyApp> {
bool _shouldShow = false;
bool _listenerRegistered = false;
Map<String, dynamic> _allFlags = {};
bool _listenerAllFlagsRegistered = false;
LaunchdarklyFlutter launchdarklyFlutter;

String mobileKey = 'YOUR_MOBILE_KEY';
Expand Down Expand Up @@ -90,10 +91,41 @@ class _MyAppState extends State<MyApp> {
SizedBox(height: 30.0,),
RaisedButton(
onPressed: () async {
_verifyAllFlags();
_verifyAllFlags([]);
},
child: Text('Verify all flags'),
),
RaisedButton(
onPressed: () async {
if (_listenerAllFlagsRegistered) {
try {
setState(() {
_listenerAllFlagsRegistered = false;
});
await launchdarklyFlutter
.unregisterAllFlagsListener('allFlags');
} on PlatformException {
setState(() {
_listenerAllFlagsRegistered = true;
});
}
} else {
try {
setState(() {
_listenerAllFlagsRegistered = true;
});
await launchdarklyFlutter.registerAllFlagsListener('allFlags', _verifyAllFlags);
} on PlatformException {
setState(() {
_listenerAllFlagsRegistered = false;
});
}
}
},
child: Text(_listenerAllFlagsRegistered
? 'Unregister All Flags listener'
: 'Register All Flags listener'),
),
Text('All flags: $_allFlags\n'),
],
),
Expand Down Expand Up @@ -121,7 +153,7 @@ class _MyAppState extends State<MyApp> {
});
}

void _verifyAllFlags() async {
void _verifyAllFlags(List<String> flagKeys) async {
Map<String, dynamic> allFlags;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
Expand Down
39 changes: 37 additions & 2 deletions ios/Classes/SwiftLaunchdarklyFlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import LaunchDarkly

result(allFlags)

}else if(call.method == "registerFeatureFlagListener") {
} else if(call.method == "registerFeatureFlagListener") {

let flagKey = arguments["flagKey"] as? String ?? ""

Expand All @@ -91,7 +91,9 @@ import LaunchDarkly
LDClient.shared.observe(keys: [flagKey], owner: flagObserverOwner, handler: { (changedFlags) in
if changedFlags[flagKey] != nil {
let flagKeyMap = ["flagKey": flagKey]
FlutterChannel.shared.channel?.invokeMethod("callbackRegisterFeatureFlagListener", arguments: flagKeyMap)
DispatchQueue.main.async {
FlutterChannel.shared.channel?.invokeMethod("callbackRegisterFeatureFlagListener", arguments: flagKeyMap)
}
}
})

Expand All @@ -110,6 +112,39 @@ import LaunchDarkly
return
}

result(false)
} else if(call.method == "registerAllFlagsListener") {

let listenerId = arguments["listenerId"] as? String ?? ""

let allFlagsObserverOwner = listenerId as LDObserverOwner

LDClient.shared.observeAll(owner: allFlagsObserverOwner) { (changedFlags) in
var allFlagsChanged = [String]();
for (key, _) in changedFlags {
allFlagsChanged.append(key)
}
let flagKeyMap = ["flagKeys": allFlagsChanged]
DispatchQueue.main.async {
FlutterChannel.shared.channel?.invokeMethod("callbackAllFlagsListener", arguments: flagKeyMap)
}
}

FlutterChannel.shared.listeners[listenerId] = allFlagsObserverOwner;

result(true)

} else if(call.method == "unregisterAllFlagsListener") {

let listenerId = arguments["listenerId"] as? String ?? ""

if (FlutterChannel.shared.listeners[listenerId] != nil) {
LDClient.shared.stopObserving(owner: FlutterChannel.shared.listeners[listenerId] ?? listenerId as LDObserverOwner)
FlutterChannel.shared.listeners.removeValue(forKey: listenerId)
result(true)
return
}

result(false)
} else {
result(FlutterMethodNotImplemented)
Expand Down
2 changes: 1 addition & 1 deletion ios/launchdarkly_flutter.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'launchdarkly_flutter'
s.version = '0.3.0'
s.version = '0.4.0'
s.summary = 'A Flutter LaunchDarkly SDK.'
s.description = <<-DESC
This is an unofficial LaunchDarkly SDK for Flutter, for anyone willing to use LaunchDarkly in a Flutter app.
Expand Down
101 changes: 86 additions & 15 deletions lib/launchdarkly_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import 'package:flutter/services.dart';
/// Client for accessing LaunchDarkly's Feature Flag system.
class LaunchdarklyFlutter {
Map<String, void Function(String)> flagListeners;
Map<String, void Function(List<String>)> allFlagsListeners;
static const MethodChannel _channel =
const MethodChannel('launchdarkly_flutter');

/// Constructor for the Client for accessing LaunchDarkly's Feature Flag system.
/// The main entry point.
/// [flagListeners] (optional) is the map of flag keys and callbacks.
LaunchdarklyFlutter({this.flagListeners}) {
LaunchdarklyFlutter({this.flagListeners, this.allFlagsListeners}) {
flagListeners ??= {};
allFlagsListeners ??= {};

_channel.setMethodCallHandler(handlerMethodCalls);
}
Expand All @@ -21,15 +23,44 @@ class LaunchdarklyFlutter {
Future<dynamic> handlerMethodCalls(MethodCall call) async {
switch (call.method) {
case 'callbackRegisterFeatureFlagListener':
if (call.arguments.containsKey('flagKey')) {
String flagKey = call.arguments['flagKey'];
if (flagListeners.containsKey(flagKey)) {
Function(String) listener = flagListeners[flagKey];
listener(flagKey);
return true;
}
if (call.arguments == null) {
return false;
}
return false;

if (!call.arguments.containsKey('flagKey')) {
return false;
}

String flagKey = call.arguments['flagKey'];

if (!flagListeners.containsKey(flagKey)) {
return false;
}

Function(String) listener = flagListeners[flagKey];
if (listener != null) listener(flagKey);
return true;

case 'callbackAllFlagsListener':
if (call.arguments == null) {
return false;
}

if (!call.arguments.containsKey('flagKeys')) {
return false;
}

List<String> flagKeys = List<String>.from(call.arguments['flagKeys']);

if (allFlagsListeners.isEmpty) {
return false;
}

allFlagsListeners.values.forEach((allFlagsListener) {
if (allFlagsListener != null) allFlagsListener(flagKeys);
});

return true;
default:
throw MissingPluginException();
}
Expand Down Expand Up @@ -91,19 +122,20 @@ class LaunchdarklyFlutter {
///
/// [flagKey] the flag key to attach the listener to
/// [callback] the listener to attach to the flag key
Future<void> registerFeatureFlagListener(
Future<bool> registerFeatureFlagListener(
String flagKey, void Function(String) callback) async {
if (flagKey == null || callback == null) {
return;
return false;
}

if (flagListeners.containsKey(flagKey)) {
flagListeners[flagKey] = callback;
} else {
flagListeners[flagKey] = callback;
await _channel.invokeMethod(
'registerFeatureFlagListener', <String, dynamic>{'flagKey': flagKey});
return true;
}

flagListeners[flagKey] = callback;
return await _channel.invokeMethod(
'registerFeatureFlagListener', <String, dynamic>{'flagKey': flagKey});
}

/// Unregisters the existing callback for the flagKey.
Expand All @@ -129,4 +161,43 @@ class LaunchdarklyFlutter {
Map<String, dynamic>.from(await _channel.invokeMethod('allFlags'));
return allFlags;
}

/// Registers a callback to be called when a flag update is processed by the
/// SDK.
///
/// [listenerId] the id to attach the listener to
/// [callback] the listener to attach to the listenerId
Future<bool> registerAllFlagsListener(
String listenerId, void Function(List<String>) callback) async {
if (listenerId == null || callback == null) {
return false;
}

if (allFlagsListeners.containsKey(listenerId)) {
allFlagsListeners[listenerId] = callback;
return true;
}

allFlagsListeners[listenerId] = callback;
return await _channel.invokeMethod('registerAllFlagsListener',
<String, dynamic>{'listenerId': listenerId});
}

/// Unregisters a callback so it will no longer be called on flag updates.
///
/// [listenerId] the id to remove the listener from
Future<bool> unregisterAllFlagsListener(String listenerId) async {
if (listenerId == null) {
return false;
}

if (!allFlagsListeners.containsKey(listenerId) ||
allFlagsListeners[listenerId] == null) {
return false;
}

allFlagsListeners.remove(listenerId);
return await _channel.invokeMethod('unregisterAllFlagsListener',
<String, dynamic>{'listenerId': listenerId});
}
}
Loading

0 comments on commit 0208dc3

Please sign in to comment.