From 357dde1ca52aae8331b54f10fe3e76117cb65cec Mon Sep 17 00:00:00 2001 From: mikechoch Date: Thu, 17 Oct 2019 16:47:24 -0700 Subject: [PATCH 1/9] Adding outcome events for Android and stubbing iOS methods * Android outcome events is released in 3.12.0 * iOS is not released yet so stubbing for now --- android/build.gradle | 12 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../OneSignalOutcomeEventsController.java | 113 ++++++++++++++++++ .../onesignal/flutter/OneSignalPlugin.java | 1 + .../flutter/OneSignalTagsController.java | 1 + example/android/app/build.gradle | 8 +- example/android/build.gradle | 2 +- example/android/gradle.properties | 6 + .../gradle/wrapper/gradle-wrapper.properties | 4 +- example/lib/main.dart | 17 +++ .../OneSignalInAppMessagesController.m | 1 - .../OneSignalOutcomeEventsController.h | 35 ++++++ .../OneSignalOutcomeEventsController.m | 71 +++++++++++ ios/Classes/OneSignalPlugin.m | 2 + lib/onesignal_flutter.dart | 19 +++ 15 files changed, 280 insertions(+), 16 deletions(-) create mode 100644 android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java create mode 100644 ios/Classes/OneSignalOutcomeEventsController.h create mode 100644 ios/Classes/OneSignalOutcomeEventsController.m diff --git a/android/build.gradle b/android/build.gradle index a7ecf127..f0d949e7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.onesignal.flutter' -version '2.1.0' +version '2.2.0' buildscript { repositories { @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.5.0' } } @@ -34,7 +34,7 @@ android { } dependencies { - compile('com.onesignal:OneSignal:3.11.4') + api 'com.onesignal:OneSignal:3.12.0' } // Adds required manifestPlaceholders keys to allow mainifest merge gradle step to complete @@ -42,8 +42,8 @@ dependencies { // Google project number / FCM Sender ID will be pulled in from the OneSignal dashbaord class DefaultManifestPlaceHolders { static final MANIFEST_PLACEHOLDERS_DEFAULTS = [ - onesignal_app_id: '', - onesignal_google_project_number: 'REMOTE' + onesignal_app_id: '${onesignal_app_id}', + onesignal_google_project_number: '${onesignal_google_project_number}' ] static void addManifestToAppProject(Project proj) { @@ -62,7 +62,7 @@ class DefaultManifestPlaceHolders { } rootProject.childProjects.each { projName, proj -> - if (projName != 'app') + if (projName != 'app' && projName != 'onesignal_flutter') return if (proj.hasProperty('android')) { diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 991f791e..ad9536e3 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jul 16 17:30:59 PDT 2018 +#Thu Oct 17 14:56:12 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java new file mode 100644 index 00000000..0eb77a79 --- /dev/null +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -0,0 +1,113 @@ +package com.onesignal.flutter; + +import com.onesignal.OneSignal; + +import java.util.concurrent.atomic.AtomicBoolean; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.PluginRegistry.Registrar; + +class OSFlutterOutcomeEventsHandler extends FlutterRegistrarResponder implements OneSignal.OutcomeCallback { + private Result result; + + // the tags callbacks can in some instances be called more than once + // ie. cached vs. server response. + // this property guarantees the callback will never be called more than once. + private AtomicBoolean replySubmitted = new AtomicBoolean(false); + + OSFlutterOutcomeEventsHandler(PluginRegistry.Registrar flutterRegistrar, MethodChannel channel, Result result) { + this.flutterRegistrar = flutterRegistrar; + this.channel = channel; + this.result = result; + } + + @Override + public void onOutcomeSuccess(String name) { + if (this.replySubmitted.getAndSet(true)) + return; + + replySuccess(result, name); + } + + @Override + public void onOutcomeFail(int statusCode, String response) { + if (this.replySubmitted.getAndSet(true)) + return; + + replyError(result,"OneSignal", "Encountered an error sending outcome with code: " + statusCode, response); + } + +} + +public class OneSignalOutcomeEventsController extends FlutterRegistrarResponder implements MethodCallHandler { + private MethodChannel channel; + private Registrar registrar; + + static void registerWith(Registrar registrar) { + OneSignalOutcomeEventsController controller = new OneSignalOutcomeEventsController(); + controller.registrar = registrar; + controller.channel = new MethodChannel(registrar.messenger(), "OneSignal#outcomes"); + controller.channel.setMethodCallHandler(controller); + controller.flutterRegistrar = registrar; + } + + @Override + public void onMethodCall(MethodCall call, Result result) { + if (call.method.contentEquals("OneSignal#sendOutcome")) + this.sendOutcome(call, result); + else if (call.method.contentEquals("OneSignal#sendUniqueOutcome")) + this.sendUniqueOutcome(call, result); + else if (call.method.contentEquals("OneSignal#sendOutcomeWithValue")) + this.sendOutcomeWithValue(call, result); + else + replyNotImplemented(result); + } + + private void sendOutcome(MethodCall call, Result result) { + String name = (String) call.arguments; + + if (name == null || name.isEmpty()) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); + replySuccess(result, null); + return; + } + + OneSignal.sendOutcome(name, new OSFlutterOutcomeEventsHandler(registrar, channel, result)); + } + + private void sendUniqueOutcome(MethodCall call, Result result) { + String name = (String) call.arguments; + + if (name == null || name.isEmpty()) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); + replySuccess(result, null); + return; + } + + OneSignal.sendUniqueOutcome(name, new OSFlutterOutcomeEventsHandler(registrar, channel, result)); + } + + private void sendOutcomeWithValue(MethodCall call, Result result) { + String name = call.argument("outcome_name"); + Double value = call.argument("outcome_value"); + + if (name == null || name.isEmpty()) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); + replySuccess(result, null); + return; + } + + if (value == null) { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome value must not be null"); + replySuccess(result, null); + return; + } + + OneSignal.sendOutcomeWithValue(name, value.floatValue(), new OSFlutterOutcomeEventsHandler(registrar, channel, result)); + } + +} \ No newline at end of file diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalPlugin.java b/android/src/main/java/com/onesignal/flutter/OneSignalPlugin.java index 3c11810e..8bce4968 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalPlugin.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalPlugin.java @@ -77,6 +77,7 @@ public boolean onViewDestroy(FlutterNativeView flutterNativeView) { OneSignalTagsController.registerWith(registrar); OneSignalInAppMessagingController.registerWith(registrar); + OneSignalOutcomeEventsController.registerWith(registrar); } @Override diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalTagsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalTagsController.java index 12bb0ab3..a7793ee0 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalTagsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalTagsController.java @@ -79,6 +79,7 @@ static void registerWith(Registrar registrar) { controller.registrar = registrar; controller.channel = new MethodChannel(registrar.messenger(), "OneSignal#tags"); controller.channel.setMethodCallHandler(controller); + controller.flutterRegistrar = registrar; } @Override diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index cc0078ab..b8b6e592 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -29,7 +29,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 27 + compileSdkVersion 28 lintOptions { disable 'InvalidPackage' @@ -39,7 +39,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.onesignal.onesignalexample" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -60,6 +60,6 @@ flutter { dependencies { testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } diff --git a/example/android/build.gradle b/example/android/build.gradle index 23d41d03..e0d7ae2c 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' + classpath 'com.android.tools.build:gradle:3.5.0' } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 8bd86f68..8b001ac7 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1 +1,7 @@ org.gradle.jvmargs=-Xmx1536M + +android.enableR8=true +android.enableD8=true + +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 9372d0f3..5e5fb0a2 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Thu Oct 17 15:00:44 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index 06de1bdd..9ee73b5b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -91,6 +91,9 @@ class _MyAppState extends State { // Some examples of how to use In App Messaging public methods with OneSignal SDK oneSignalInAppMessagingTriggerExamples(); + + // Some examples of how to use Outcome Events public methods with OneSignal SDK + oneSignalOutcomeEventsExamples(); } void _handleGetTags() { @@ -265,6 +268,20 @@ class _MyAppState extends State { OneSignal.shared.pauseInAppMessages(false); } + oneSignalOutcomeEventsExamples() async { + // Send a normal outcome and get a reply with the name of the outcome + String normalOutcome = await OneSignal.shared.sendOutcome("normal"); + print(normalOutcome); + + // Send a unique outcome and get a reply with the name of the outcome + String uniqueOutcome = await OneSignal.shared.sendUniqueOutcome("unique"); + print(uniqueOutcome); + + // Send an outcome with a value and get a reply with the name of the outcome + String valueOutcome = await OneSignal.shared.sendOutcomeWithValue("value", 3.2); + print(valueOutcome); + } + @override Widget build(BuildContext context) { return new MaterialApp( diff --git a/ios/Classes/OneSignalInAppMessagesController.m b/ios/Classes/OneSignalInAppMessagesController.m index eb2c60fa..8476ba02 100644 --- a/ios/Classes/OneSignalInAppMessagesController.m +++ b/ios/Classes/OneSignalInAppMessagesController.m @@ -68,7 +68,6 @@ - (void)removeTriggerForKey:(FlutterMethodCall *)call withResult:(FlutterResult) NSString *key = call.arguments; [OneSignal removeTriggerForKey:key]; result(@[]); - } - (void)removeTriggersForKeys:(FlutterMethodCall *)call withResult:(FlutterResult)result { diff --git a/ios/Classes/OneSignalOutcomeEventsController.h b/ios/Classes/OneSignalOutcomeEventsController.h new file mode 100644 index 00000000..545dbf9d --- /dev/null +++ b/ios/Classes/OneSignalOutcomeEventsController.h @@ -0,0 +1,35 @@ +/** + * Modified MIT License + * + * Copyright 2019 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import +#import + +@interface OneSignalOutcomeEventsController : NSObject + +@property (strong, nonatomic) FlutterMethodChannel *channel; + +@end diff --git a/ios/Classes/OneSignalOutcomeEventsController.m b/ios/Classes/OneSignalOutcomeEventsController.m new file mode 100644 index 00000000..9acfed80 --- /dev/null +++ b/ios/Classes/OneSignalOutcomeEventsController.m @@ -0,0 +1,71 @@ +/** + * Modified MIT License + * + * Copyright 2019 OneSignal + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. All copies of substantial portions of the Software may only be used in connection + * with services provided by OneSignal. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "OneSignalOutcomeEventsController.h" +#import +#import "OneSignalCategories.h" + +@implementation OneSignalOutcomeEventsController ++ (void)registerWithRegistrar:(NSObject*)registrar { + OneSignalInAppMessagesController *instance = [OneSignalInAppMessagesController new]; + + instance.channel = [FlutterMethodChannel + methodChannelWithName:@"OneSignal#outcomes" + binaryMessenger:[registrar messenger]]; + + [registrar addMethodCallDelegate:instance channel:instance.channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + if ([@"OneSignal#sendOutcome" isEqualToString:call.method]) { + [self sendOutcome:call withResult:result]; + } else if ([@"OneSignal#sendUniqueOutcome" isEqualToString:call.method]) { + [self sendUniqueOutcome:call withResult:result]; + } else if ([@"OneSignal#sendOutcomeWithValue" isEqualToString:call.method]) { + [self sendOutcomeWithValue:call withResult:result]; + } else { + result(FlutterMethodNotImplemented); + } +} + +- (void)sendOutcome:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *name = call.arguments; + result(FlutterMethodNotImplemented); +} + +- (void)sendUniqueOutcome:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *name = call.arguments; + result(FlutterMethodNotImplemented); +} + +- (void)sendOutcomeWithValue:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *name = call.arguments[@"outcome_name"]; + NSNumber *value = call.arguments[@"outcome_value"]; + result(FlutterMethodNotImplemented); +} + +@end diff --git a/ios/Classes/OneSignalPlugin.m b/ios/Classes/OneSignalPlugin.m index dd2906ab..eaedc45e 100644 --- a/ios/Classes/OneSignalPlugin.m +++ b/ios/Classes/OneSignalPlugin.m @@ -29,6 +29,7 @@ #import "OneSignalCategories.h" #import "OneSignalTagsController.h" #import "OneSignalInAppMessagesController.h" +#import "OneSignalOutcomeEventsController.h" @interface OneSignalPlugin () @@ -95,6 +96,7 @@ + (void)registerWithRegistrar:(NSObject*)registrar { [OneSignalTagsController registerWithRegistrar:registrar]; [OneSignalInAppMessagesController registerWithRegistrar:registrar]; + [OneSignalOutcomeEventsController registerWithRegistrar:registrar]; } - (void)addObservers { diff --git a/lib/onesignal_flutter.dart b/lib/onesignal_flutter.dart index 717b3a61..0899e9f0 100644 --- a/lib/onesignal_flutter.dart +++ b/lib/onesignal_flutter.dart @@ -35,6 +35,7 @@ class OneSignal { MethodChannel _channel = const MethodChannel('OneSignal'); MethodChannel _tagsChannel = const MethodChannel('OneSignal#tags'); MethodChannel _inAppMessagesChannel = const MethodChannel('OneSignal#inAppMessages'); + MethodChannel _outcomesChannel = const MethodChannel('OneSignal#outcomes'); // event handlers ReceivedNotificationHandler _onReceivedNotification; @@ -315,6 +316,24 @@ class OneSignal { return await _inAppMessagesChannel.invokeMethod("OneSignal#pauseInAppMessages", pause); } + /// Send a normal outcome event for the current session and notifications with the attribution window + /// Counted each time sent successfully, failed ones will be cached and reattempted in future + Future sendOutcome(String name) async { + return await _outcomesChannel.invokeMethod("OneSignal#sendOutcome", name); + } + + /// Send a unique outcome event for the current session and notifications with the attribution window + /// Counted once per notification when sent successfully, failed ones will be cached and reattempted in future + Future sendUniqueOutcome(String name) async { + return await _outcomesChannel.invokeMethod("OneSignal#sendUniqueOutcome", name); + } + + /// Send an outcome event with a value for the current session and notifications with the attribution window + /// Counted each time sent successfully, failed ones will be cached and reattempted in future + Future sendOutcomeWithValue(String name, double value) async { + return await _outcomesChannel.invokeMethod("OneSignal#sendOutcomeWithValue", {"outcome_name" : name, "outcome_value" : value}); + } + // Private function that gets called by ObjC/Java Future _handleMethod(MethodCall call) async { if (call.method == 'OneSignal#handleReceivedNotification' && From 24034715d87480f3267607f10cf5b210c35145b4 Mon Sep 17 00:00:00 2001 From: mikechoch Date: Fri, 18 Oct 2019 14:07:55 -0700 Subject: [PATCH 2/9] Stubbed out iOS methods * Adding nil inside of 'result(nil)' instead of 'result(@[])' * Updated native iOS to '2.11.2' and Flutter version to '2.2.0' --- example/pubspec.yaml | 4 +-- .../OneSignalInAppMessagesController.m | 8 +++--- .../OneSignalOutcomeEventsController.m | 25 ++++++++++-------- ios/Classes/OneSignalPlugin.m | 26 +++++++++---------- ios/onesignal_flutter.podspec | 4 +-- pubspec.yaml | 2 +- 6 files changed, 36 insertions(+), 33 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e303330b..263ae18c 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - - onesignal_flutter: ^2.0.1 + + onesignal_flutter: ^2.2.0 # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec diff --git a/ios/Classes/OneSignalInAppMessagesController.m b/ios/Classes/OneSignalInAppMessagesController.m index 8476ba02..0872e725 100644 --- a/ios/Classes/OneSignalInAppMessagesController.m +++ b/ios/Classes/OneSignalInAppMessagesController.m @@ -61,25 +61,25 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result - (void)addTriggers:(FlutterMethodCall *)call withResult:(FlutterResult)result { NSDictionary *triggers = call.arguments; [OneSignal addTriggers:triggers]; - result(@[]); + result(nil); } - (void)removeTriggerForKey:(FlutterMethodCall *)call withResult:(FlutterResult)result { NSString *key = call.arguments; [OneSignal removeTriggerForKey:key]; - result(@[]); + result(nil); } - (void)removeTriggersForKeys:(FlutterMethodCall *)call withResult:(FlutterResult)result { NSArray *keys = call.arguments; [OneSignal removeTriggersForKeys:keys]; - result(@[]); + result(nil); } - (void)pauseInAppMessages:(FlutterMethodCall *)call withResult:(FlutterResult)result { BOOL pause = [call.arguments boolValue]; [OneSignal pauseInAppMessages:pause]; - result(@[]); + result(nil); } @end diff --git a/ios/Classes/OneSignalOutcomeEventsController.m b/ios/Classes/OneSignalOutcomeEventsController.m index 9acfed80..0b0bf855 100644 --- a/ios/Classes/OneSignalOutcomeEventsController.m +++ b/ios/Classes/OneSignalOutcomeEventsController.m @@ -31,7 +31,7 @@ @implementation OneSignalOutcomeEventsController + (void)registerWithRegistrar:(NSObject*)registrar { - OneSignalInAppMessagesController *instance = [OneSignalInAppMessagesController new]; + OneSignalOutcomeEventsController *instance = [OneSignalOutcomeEventsController new]; instance.channel = [FlutterMethodChannel methodChannelWithName:@"OneSignal#outcomes" @@ -52,20 +52,23 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } } -- (void)sendOutcome:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *name = call.arguments; - result(FlutterMethodNotImplemented); +- (void)sendOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { +// NSString *name = call.arguments; + [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcome() not implemented for iOS yet!"]; + result(nil); } -- (void)sendUniqueOutcome:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *name = call.arguments; - result(FlutterMethodNotImplemented); +- (void)sendUniqueOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { +// NSString *name = call.arguments; + [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendUniqueOutcome() not implemented for iOS yet!"]; + result(nil); } -- (void)sendOutcomeWithValue:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *name = call.arguments[@"outcome_name"]; - NSNumber *value = call.arguments[@"outcome_value"]; - result(FlutterMethodNotImplemented); +- (void)sendOutcomeWithValue:(FlutterMethodCall *)call withResult:(FlutterResult)result { +// NSString *name = call.arguments[@"outcome_name"]; +// NSNumber *value = call.arguments[@"outcome_value"]; + [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcomeWithValue() not implemented for iOS yet!"]; + result(nil); } @end diff --git a/ios/Classes/OneSignalPlugin.m b/ios/Classes/OneSignalPlugin.m index eaedc45e..ee8857ef 100644 --- a/ios/Classes/OneSignalPlugin.m +++ b/ios/Classes/OneSignalPlugin.m @@ -169,24 +169,24 @@ - (void)initOneSignal:(FlutterMethodCall *)call withResult:(FlutterResult)result } else { [self addObservers]; } - result(@[]); + result(nil); } - (void)setOneSignalLogLevel:(FlutterMethodCall *)call withResult:(FlutterResult)result { ONE_S_LOG_LEVEL consoleLogLevel = (ONE_S_LOG_LEVEL)[call.arguments[@"console"] intValue]; ONE_S_LOG_LEVEL visualLogLevel = (ONE_S_LOG_LEVEL)[call.arguments[@"visual"] intValue]; [OneSignal setLogLevel:consoleLogLevel visualLevel:visualLogLevel]; - result(@[]); + result(nil); } - (void)oneSignalLog:(FlutterMethodCall *)call withResult:(FlutterResult)result { [OneSignal onesignal_Log:(ONE_S_LOG_LEVEL)[call.arguments[@"logLevel"] integerValue] message:(NSString *)call.arguments[@"message"]]; - result(@[]); + result(nil); } - (void)setRequiresUserPrivacyConsent:(FlutterMethodCall *)call withResult:(FlutterResult)result { [OneSignal setRequiresUserPrivacyConsent:[call.arguments[@"required"] boolValue]]; - result(@[]); + result(nil); } - (void)setConsentStatus:(FlutterMethodCall *)call withResult:(FlutterResult)result { @@ -196,13 +196,13 @@ - (void)setConsentStatus:(FlutterMethodCall *)call withResult:(FlutterResult)res if (self.waitingForUserConsent && granted) [self addObservers]; - result(@[]); + result(nil); } - (void)setInFocusDisplayType:(FlutterMethodCall *)call withResult:(FlutterResult)result { OSNotificationDisplayType displayType = (OSNotificationDisplayType)[call.arguments[@"displayType"] intValue]; [OneSignal setInFocusDisplayType:displayType]; - result(@[]); + result(nil); } - (void)promptPermission:(FlutterMethodCall *)call withResult:(FlutterResult)result { @@ -214,7 +214,7 @@ - (void)promptPermission:(FlutterMethodCall *)call withResult:(FlutterResult)res - (void)setSubscription:(FlutterMethodCall *)call withResult:(FlutterResult)result { BOOL subscribe = [call.arguments boolValue]; [OneSignal setSubscription:subscribe]; - result(@[]); + result(nil); } - (void)postNotification:(FlutterMethodCall *)call withResult:(FlutterResult)result { @@ -227,13 +227,13 @@ - (void)postNotification:(FlutterMethodCall *)call withResult:(FlutterResult)res - (void)promptLocation:(FlutterMethodCall *)call withResult:(FlutterResult)result { [OneSignal promptLocation]; - result(@[]); + result(nil); } - (void)setLocationShared:(FlutterMethodCall *)call withResult:(FlutterResult)result { BOOL locationShared = [call.arguments boolValue]; [OneSignal setLocationShared:locationShared]; - result(@[]); + result(nil); } - (void)setEmail:(FlutterMethodCall *)call withResult:(FlutterResult)result { @@ -245,7 +245,7 @@ - (void)setEmail:(FlutterMethodCall *)call withResult:(FlutterResult)result { emailAuthHashToken = nil; [OneSignal setEmail:email withEmailAuthHashToken:emailAuthHashToken withSuccess:^{ - result(@[]); + result(nil); } withFailure:^(NSError *error) { result(error.flutterError); }]; @@ -253,7 +253,7 @@ - (void)setEmail:(FlutterMethodCall *)call withResult:(FlutterResult)result { - (void)logoutEmail:(FlutterMethodCall *)call withResult:(FlutterResult)result { [OneSignal logoutEmailWithSuccess:^{ - result(@[]); + result(nil); } withFailure:^(NSError *error) { result(error.flutterError); }]; @@ -264,12 +264,12 @@ - (void)setExternalUserId:(FlutterMethodCall *)call withResult:(FlutterResult)re if (externalId == [NSNull null]) externalId = nil; [OneSignal setExternalUserId:externalId]; - result(@[]); + result(nil); } - (void)removeExternalUserId:(FlutterMethodCall *)call withResult:(FlutterResult)result { [OneSignal removeExternalUserId]; - result(@[]); + result(nil); } - (void)initNotificationOpenedHandlerParams { diff --git a/ios/onesignal_flutter.podspec b/ios/onesignal_flutter.podspec index 31cb06cb..b7985d7d 100644 --- a/ios/onesignal_flutter.podspec +++ b/ios/onesignal_flutter.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'onesignal_flutter' - s.version = '2.1.0' + s.version = '2.2.0' s.summary = 'The OneSignal Flutter SDK' s.description = 'Allows you to easily add OneSignal to your flutter projects, to make sending and handling push notifications easy' s.homepage = 'https://www.onesignal.com' @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'OneSignal', '2.11.0' + s.dependency 'OneSignal', '2.11.2' s.ios.deployment_target = '8.0' s.static_framework = true end diff --git a/pubspec.yaml b/pubspec.yaml index b071a41e..918b44bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: onesignal_flutter description: OneSignal is a free push notification service for mobile apps. This plugin makes it easy to integrate your flutter app with OneSignal -version: 2.1.0 +version: 2.2.0 author: Brad Hesse , Josh Kasten homepage: https://github.com/OneSignal/OneSignal-Flutter-SDK From 0b11bf594d230bf630ffe910270cb8325c56158b Mon Sep 17 00:00:00 2001 From: mikechoch Date: Fri, 18 Oct 2019 15:31:15 -0700 Subject: [PATCH 3/9] Added then and catchError statements for sending outcome examples --- example/lib/main.dart | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 9ee73b5b..126c2d3c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -269,17 +269,26 @@ class _MyAppState extends State { } oneSignalOutcomeEventsExamples() async { - // Send a normal outcome and get a reply with the name of the outcome - String normalOutcome = await OneSignal.shared.sendOutcome("normal"); - print(normalOutcome); + // Send a normal outcome and get a reply with the name of the outcome + OneSignal.shared.sendOutcome("normal").then((name) { + print("Successfully sent outcome with name: $name"); + }).catchError((error) { + print("Failed to send outcome with error: $error"); + }); - // Send a unique outcome and get a reply with the name of the outcome - String uniqueOutcome = await OneSignal.shared.sendUniqueOutcome("unique"); - print(uniqueOutcome); + // Send a unique outcome and get a reply with the name of the outcome + OneSignal.shared.sendUniqueOutcome("unique").then((name) { + print("Successfully sent outcome with name: $name"); + }).catchError((error) { + print("Failed to send outcome with error: $error"); + }); - // Send an outcome with a value and get a reply with the name of the outcome - String valueOutcome = await OneSignal.shared.sendOutcomeWithValue("value", 3.2); - print(valueOutcome); + // Send an outcome with a value and get a reply with the name of the outcome + OneSignal.shared.sendOutcomeWithValue("value", 3.2).then((name) { + print("Successfully sent outcome with name: $name"); + }).catchError((error) { + print("Failed to send outcome with error: $error"); + }); } @override From 12a99e916cd75052a278b12dac8a2a04bb3d652e Mon Sep 17 00:00:00 2001 From: mikechoch Date: Wed, 23 Oct 2019 14:05:35 -0700 Subject: [PATCH 4/9] Added all changes for outcomes * public methods connected for 'sendOutcome', 'sendUniqueOutcome', and 'sendOutcomeWithValue' * Callbacks and awaits all work for sending outcomes (removed failure from native SDK, now only onSuccess exists and returns a OutcomeEvent) * Android is updated to 3.12.1 (adding outcomes) and iOS is on 2.11.2 waiting for 2.12.0 for outcomes work --- android/build.gradle | 2 +- .../OneSignalOutcomeEventsController.java | 14 +--- .../flutter/OneSignalSerializer.java | 12 +++ example/lib/main.dart | 30 ++++---- ios/Classes/OneSignalCategories.h | 5 ++ ios/Classes/OneSignalCategories.m | 15 ++++ .../OneSignalOutcomeEventsController.m | 6 +- lib/onesignal_flutter.dart | 17 +++-- lib/src/defines.dart | 7 ++ lib/src/outcome_event.dart | 75 +++++++++++++++++++ lib/src/utils.dart | 11 +++ 11 files changed, 158 insertions(+), 36 deletions(-) create mode 100644 lib/src/outcome_event.dart diff --git a/android/build.gradle b/android/build.gradle index f0d949e7..8d29afc5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,7 +34,7 @@ android { } dependencies { - api 'com.onesignal:OneSignal:3.12.0' + api 'com.onesignal:OneSignal:3.12.1' } // Adds required manifestPlaceholders keys to allow mainifest merge gradle step to complete diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java index 0eb77a79..01ac52a8 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -1,6 +1,7 @@ package com.onesignal.flutter; import com.onesignal.OneSignal; +import com.onesignal.OutcomeEvent; import java.util.concurrent.atomic.AtomicBoolean; @@ -26,21 +27,12 @@ class OSFlutterOutcomeEventsHandler extends FlutterRegistrarResponder implements } @Override - public void onOutcomeSuccess(String name) { + public void onSuccess(OutcomeEvent outcomeEvent) { if (this.replySubmitted.getAndSet(true)) return; - replySuccess(result, name); + replySuccess(result, OneSignalSerializer.convertOutcomeEventToMap(outcomeEvent)); } - - @Override - public void onOutcomeFail(int statusCode, String response) { - if (this.replySubmitted.getAndSet(true)) - return; - - replyError(result,"OneSignal", "Encountered an error sending outcome with code: " + statusCode, response); - } - } public class OneSignalOutcomeEventsController extends FlutterRegistrarResponder implements MethodCallHandler { diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java index ebab687a..786cfa15 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java @@ -197,6 +197,18 @@ static HashMap convertInAppMessageClickedActionToMap(OSInAppMess return hash; } + static HashMap convertOutcomeEventToMap(OutcomeEvent outcomeEvent) { + HashMap hash = new HashMap<>(); + + hash.put("session", outcomeEvent.getSession().toString()); + hash.put("notification_ids", outcomeEvent.getNotificationIds().toString()); + hash.put("name", outcomeEvent.getName()); + hash.put("timestamp", outcomeEvent.getTimestamp()); + hash.put("weight", String.valueOf(outcomeEvent.getWeight())); + + return hash; + } + private static HashMap convertAndroidBackgroundImageLayoutToMap(BackgroundImageLayout layout) { HashMap hash = new HashMap<>(); diff --git a/example/lib/main.dart b/example/lib/main.dart index 126c2d3c..763e451b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -78,7 +78,7 @@ class _MyAppState extends State { // NOTE: Replace with your own app ID from https://www.onesignal.com await OneSignal.shared - .init("b2f7f966-d8cc-11e4-bed1-df8f05be55ba", iOSSettings: settings); + .init("77e32082-ea27-42e3-a898-c72e141824ef", iOSSettings: settings); OneSignal.shared .setInFocusDisplayType(OSNotificationDisplayType.notification); @@ -270,24 +270,24 @@ class _MyAppState extends State { oneSignalOutcomeEventsExamples() async { // Send a normal outcome and get a reply with the name of the outcome - OneSignal.shared.sendOutcome("normal").then((name) { - print("Successfully sent outcome with name: $name"); - }).catchError((error) { - print("Failed to send outcome with error: $error"); - }); + OneSignal.shared.sendOutcome("normal_1"); + OneSignal.shared.sendOutcome("normal_2").then((outcomeEvent) { + var json = outcomeEvent.jsonRepresentation(); + print("Successfully sent outcome event: $json"); + }); // Send a unique outcome and get a reply with the name of the outcome - OneSignal.shared.sendUniqueOutcome("unique").then((name) { - print("Successfully sent outcome with name: $name"); - }).catchError((error) { - print("Failed to send outcome with error: $error"); - }); + await OneSignal.shared.sendUniqueOutcome("unique_1"); + OneSignal.shared.sendUniqueOutcome("unique_2").then((outcomeEvent) { + var json = outcomeEvent.jsonRepresentation(); + print("Successfully sent unique outcome event: $json"); + }); // Send an outcome with a value and get a reply with the name of the outcome - OneSignal.shared.sendOutcomeWithValue("value", 3.2).then((name) { - print("Successfully sent outcome with name: $name"); - }).catchError((error) { - print("Failed to send outcome with error: $error"); + OneSignal.shared.sendOutcomeWithValue("value_1", 3.2); + OneSignal.shared.sendOutcomeWithValue("value_2", 3.9).then((outcomeEvent) { + var json = outcomeEvent.jsonRepresentation(); + print("Successfully sent outcome event with value: $json"); }); } diff --git a/ios/Classes/OneSignalCategories.h b/ios/Classes/OneSignalCategories.h index 2412840e..0a7e271e 100644 --- a/ios/Classes/OneSignalCategories.h +++ b/ios/Classes/OneSignalCategories.h @@ -47,6 +47,11 @@ - (NSDictionary *)toJson; @end +// TODO: Will reference the OSOutcomeEvent in OneSignal.h +//@interface OSOutcomeEvent (Flutter) +//- (NSDictionary *)toJson; +//@end + @interface NSError (Flutter) - (FlutterError *)flutterError; @end diff --git a/ios/Classes/OneSignalCategories.m b/ios/Classes/OneSignalCategories.m index 325a08ed..3d60b387 100644 --- a/ios/Classes/OneSignalCategories.m +++ b/ios/Classes/OneSignalCategories.m @@ -112,6 +112,21 @@ - (NSDictionary *)toJson { } @end +// TODO: Will reference the OSOutcomeEvent in OneSignal.h +//@implementation OSOutcomeEvent (Flutter) +//- (NSDictionary *)toJson { +// NSMutableDictionary *json = [NSMutableDictionary new]; +// +// json[@"session"] = self.session; +// json[@"notification_ids"] = self.notificationIds; +// json[@"name"] = self.name; +// json[@"timestamp"] = self.timestamp; +// json[@"weight"] = self.weight; +// +// return json; +//} +//@end + @implementation NSError (Flutter) - (FlutterError *)flutterError { return [FlutterError errorWithCode:[NSString stringWithFormat:@"%i", (int)self.code] message:self.localizedDescription details:nil]; diff --git a/ios/Classes/OneSignalOutcomeEventsController.m b/ios/Classes/OneSignalOutcomeEventsController.m index 0b0bf855..0416b8e9 100644 --- a/ios/Classes/OneSignalOutcomeEventsController.m +++ b/ios/Classes/OneSignalOutcomeEventsController.m @@ -55,20 +55,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result - (void)sendOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments; [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcome() not implemented for iOS yet!"]; - result(nil); + result(@{}); } - (void)sendUniqueOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments; [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendUniqueOutcome() not implemented for iOS yet!"]; - result(nil); + result(@{}); } - (void)sendOutcomeWithValue:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments[@"outcome_name"]; // NSNumber *value = call.arguments[@"outcome_value"]; [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcomeWithValue() not implemented for iOS yet!"]; - result(nil); + result(@{}); } @end diff --git a/lib/onesignal_flutter.dart b/lib/onesignal_flutter.dart index 0899e9f0..0480ab9d 100644 --- a/lib/onesignal_flutter.dart +++ b/lib/onesignal_flutter.dart @@ -7,6 +7,7 @@ import 'package:onesignal_flutter/src/utils.dart'; import 'package:onesignal_flutter/src/notification.dart'; import 'package:onesignal_flutter/src/create_notification.dart'; import 'package:onesignal_flutter/src/in_app_message.dart'; +import 'package:onesignal_flutter/src/outcome_event.dart'; export 'src/notification.dart'; export 'src/subscription.dart'; @@ -14,6 +15,7 @@ export 'src/permission.dart'; export 'src/defines.dart'; export 'src/create_notification.dart'; export 'src/in_app_message.dart'; +export 'src/outcome_event.dart'; // Handlers for various events typedef void ReceivedNotificationHandler(OSNotification notification); @@ -318,20 +320,23 @@ class OneSignal { /// Send a normal outcome event for the current session and notifications with the attribution window /// Counted each time sent successfully, failed ones will be cached and reattempted in future - Future sendOutcome(String name) async { - return await _outcomesChannel.invokeMethod("OneSignal#sendOutcome", name); + Future sendOutcome(String name) async { + Map json = await _outcomesChannel.invokeMethod("OneSignal#sendOutcome", name); + return new OSOutcomeEvent(json.cast()); } /// Send a unique outcome event for the current session and notifications with the attribution window /// Counted once per notification when sent successfully, failed ones will be cached and reattempted in future - Future sendUniqueOutcome(String name) async { - return await _outcomesChannel.invokeMethod("OneSignal#sendUniqueOutcome", name); + Future sendUniqueOutcome(String name) async { + Map json = await _outcomesChannel.invokeMethod("OneSignal#sendUniqueOutcome", name); + return new OSOutcomeEvent(json.cast()); } /// Send an outcome event with a value for the current session and notifications with the attribution window /// Counted each time sent successfully, failed ones will be cached and reattempted in future - Future sendOutcomeWithValue(String name, double value) async { - return await _outcomesChannel.invokeMethod("OneSignal#sendOutcomeWithValue", {"outcome_name" : name, "outcome_value" : value}); + Future sendOutcomeWithValue(String name, double value) async { + Map json = await _outcomesChannel.invokeMethod("OneSignal#sendOutcomeWithValue", {"outcome_name" : name, "outcome_value" : value}); + return new OSOutcomeEvent(json.cast()); } // Private function that gets called by ObjC/Java diff --git a/lib/src/defines.dart b/lib/src/defines.dart index 8392c276..488e72ee 100644 --- a/lib/src/defines.dart +++ b/lib/src/defines.dart @@ -24,6 +24,13 @@ enum OSiOSSettings { inFocusDisplayOption } +enum OSSession { + DIRECT, + INDIRECT, + UNATTRIBUTED, + DISABLED +} + /// Applies to iOS notifications only /// Determines if the badgeCount is used to increment /// the existing badge count, or sets the badge count directly diff --git a/lib/src/outcome_event.dart b/lib/src/outcome_event.dart new file mode 100644 index 00000000..27560971 --- /dev/null +++ b/lib/src/outcome_event.dart @@ -0,0 +1,75 @@ +import 'dart:convert'; +import 'package:onesignal_flutter/src/defines.dart'; +import 'package:onesignal_flutter/src/utils.dart'; + +OSSession sessionFromString(String session) { + session = session.toLowerCase(); + if (session == 'direct') { + return OSSession.DIRECT; + } else if (session == 'indirect') { + return OSSession.INDIRECT; + } else if (session == 'unattributed') { + return OSSession.UNATTRIBUTED; + } + + return OSSession.DISABLED; +} + +/// When an outcome is sent the onSuccess will return an OutcomeEvent +/// This object is converted from the native OutcomeEvent into the OSOutcomeEvent +/// for Dart to use +class OSOutcomeEvent extends JSONStringRepresentable { + + // The session when the outcome event is sent (DIRECT, INDIRECT, UNATTRIBUTED) + OSSession session; + + // List of notification ids that were sent with the outcome + List notificationIds; + + // Name of the outcome event + String name; + + // Time in millis when the outcome was sent + int timestamp; + + // Value if one exists (default 0.0) that was sent with the outcome + double weight; + + OSOutcomeEvent(Map outcome) { + // Make sure session exists + this.session = outcome["session"] == null ? + OSSession.DISABLED : + sessionFromString(outcome["session"] as String); + + // Make sure notification_ids exists + this.notificationIds = outcome["notification_ids"] == null ? + [] : + new List.from(json.decode(outcome["notification_ids"])); + + // Make sure name exists + this.name = outcome["name"] == null ? + "" : + outcome["name"] as String; + + // Make sure timestamp exists + this.timestamp = outcome["timestamp"] == null ? + 0 : + outcome["timestamp"] as int; + + // Make sure weight exists + this.weight = outcome["weight"] == null ? + 0 : + double.parse(outcome["weight"] as String); + } + + String jsonRepresentation() { + return convertToJsonString({ + 'session': convertEnumCaseToValue(this.session), + 'notification_ids': this.notificationIds, + 'name': this.name, + 'timestamp': this.timestamp, + 'weight': this.weight, + }); + } + +} diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 92b3d2f5..7ddcaa42 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -60,6 +60,17 @@ dynamic convertEnumCaseToValue(dynamic key) { return 2; } + switch (key) { + case OSSession.DIRECT: + return "DIRECT"; + case OSSession.INDIRECT: + return "INDIRECT"; + case OSSession.UNATTRIBUTED: + return "UNATTRIBUTED"; + case OSSession.DISABLED: + return "DISABLED"; + } + return key; } From 884093d1c9d6cd6d61d801c89b04eac82f846ffa Mon Sep 17 00:00:00 2001 From: mikechoch Date: Wed, 23 Oct 2019 14:10:24 -0700 Subject: [PATCH 5/9] Changed app id to demo app id --- example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 763e451b..5125a633 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -78,7 +78,7 @@ class _MyAppState extends State { // NOTE: Replace with your own app ID from https://www.onesignal.com await OneSignal.shared - .init("77e32082-ea27-42e3-a898-c72e141824ef", iOSSettings: settings); + .init("b2f7f966-d8cc-11e4-bed1-df8f05be55ba", iOSSettings: settings); OneSignal.shared .setInFocusDisplayType(OSNotificationDisplayType.notification); From bd6fbdc0e5487e76a0b45190f96e14da9b57f56a Mon Sep 17 00:00:00 2001 From: mikechoch Date: Wed, 23 Oct 2019 18:44:28 -0700 Subject: [PATCH 6/9] Added replyError for incorrect inputs to public outcome methods * Changed 'name' key to 'id' to match other wrappers and native SDK --- .../flutter/OneSignalOutcomeEventsController.java | 12 ++++-------- .../com/onesignal/flutter/OneSignalSerializer.java | 2 +- example/lib/main.dart | 6 +++--- lib/src/defines.dart | 2 +- lib/src/outcome_event.dart | 6 +++--- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java index 01ac52a8..ec1d99b1 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -63,8 +63,7 @@ private void sendOutcome(MethodCall call, Result result) { String name = (String) call.arguments; if (name == null || name.isEmpty()) { - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); - replySuccess(result, null); + replyError(result, "OneSignal", "sendOutcome() name must not be null or empty", null); return; } @@ -75,8 +74,7 @@ private void sendUniqueOutcome(MethodCall call, Result result) { String name = (String) call.arguments; if (name == null || name.isEmpty()) { - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); - replySuccess(result, null); + replyError(result, "OneSignal", "sendUniqueOutcome() name must not be null or empty", null); return; } @@ -88,14 +86,12 @@ private void sendOutcomeWithValue(MethodCall call, Result result) { Double value = call.argument("outcome_value"); if (name == null || name.isEmpty()) { - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome name must not be null or empty"); - replySuccess(result, null); + replyError(result, "OneSignal", "sendOutcomeWithValue() name must not be null or empty", null); return; } if (value == null) { - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Outcome value must not be null"); - replySuccess(result, null); + replyError(result, "OneSignal", "sendOutcomeWithValue() value must not be null", null); return; } diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java index 786cfa15..ee9f9405 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java @@ -202,7 +202,7 @@ static HashMap convertOutcomeEventToMap(OutcomeEvent outcomeEven hash.put("session", outcomeEvent.getSession().toString()); hash.put("notification_ids", outcomeEvent.getNotificationIds().toString()); - hash.put("name", outcomeEvent.getName()); + hash.put("id", outcomeEvent.getName()); hash.put("timestamp", outcomeEvent.getTimestamp()); hash.put("weight", String.valueOf(outcomeEvent.getWeight())); diff --git a/example/lib/main.dart b/example/lib/main.dart index 5125a633..f6ddc2f3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -274,14 +274,14 @@ class _MyAppState extends State { OneSignal.shared.sendOutcome("normal_2").then((outcomeEvent) { var json = outcomeEvent.jsonRepresentation(); print("Successfully sent outcome event: $json"); - }); + }); // Send a unique outcome and get a reply with the name of the outcome - await OneSignal.shared.sendUniqueOutcome("unique_1"); + OneSignal.shared.sendUniqueOutcome("unique_1"); OneSignal.shared.sendUniqueOutcome("unique_2").then((outcomeEvent) { var json = outcomeEvent.jsonRepresentation(); print("Successfully sent unique outcome event: $json"); - }); + }); // Send an outcome with a value and get a reply with the name of the outcome OneSignal.shared.sendOutcomeWithValue("value_1", 3.2); diff --git a/lib/src/defines.dart b/lib/src/defines.dart index 488e72ee..0d8ec324 100644 --- a/lib/src/defines.dart +++ b/lib/src/defines.dart @@ -1,4 +1,4 @@ -String sdkVersion = "2.0.0"; +String sdkVersion = "2.2.0"; /// Determines how notifications should be displayed enum OSNotificationDisplayType { none, alert, notification } diff --git a/lib/src/outcome_event.dart b/lib/src/outcome_event.dart index 27560971..b6ccc6b4 100644 --- a/lib/src/outcome_event.dart +++ b/lib/src/outcome_event.dart @@ -47,9 +47,9 @@ class OSOutcomeEvent extends JSONStringRepresentable { new List.from(json.decode(outcome["notification_ids"])); // Make sure name exists - this.name = outcome["name"] == null ? + this.name = outcome["id"] == null ? "" : - outcome["name"] as String; + outcome["id"] as String; // Make sure timestamp exists this.timestamp = outcome["timestamp"] == null ? @@ -66,7 +66,7 @@ class OSOutcomeEvent extends JSONStringRepresentable { return convertToJsonString({ 'session': convertEnumCaseToValue(this.session), 'notification_ids': this.notificationIds, - 'name': this.name, + 'id': this.name, 'timestamp': this.timestamp, 'weight': this.weight, }); From 6e920f9778ed73126cde64c2d13bc5e48edf6335 Mon Sep 17 00:00:00 2001 From: mikechoch Date: Wed, 23 Oct 2019 18:46:16 -0700 Subject: [PATCH 7/9] Fixed comment from tags to outcome events --- .../com/onesignal/flutter/OneSignalOutcomeEventsController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java index ec1d99b1..9390f5ed 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -15,7 +15,7 @@ class OSFlutterOutcomeEventsHandler extends FlutterRegistrarResponder implements OneSignal.OutcomeCallback { private Result result; - // the tags callbacks can in some instances be called more than once + // the outcome events callbacks can in some instances be called more than once // ie. cached vs. server response. // this property guarantees the callback will never be called more than once. private AtomicBoolean replySubmitted = new AtomicBoolean(false); From 496a51348efa26e65d2f4a7eb0e1027b8359b256 Mon Sep 17 00:00:00 2001 From: mikechoch Date: Thu, 24 Oct 2019 17:16:51 -0700 Subject: [PATCH 8/9] Updated Android SDK to 3.12.2 * Changed iOS stubs to WARN logs * Added checks for OutcomeEvent so it does not crash when parsing null or null attributes --- android/build.gradle | 2 +- .../flutter/OneSignalOutcomeEventsController.java | 11 +++++++++-- .../com/onesignal/flutter/OneSignalSerializer.java | 7 ++++++- example/lib/main.dart | 8 ++++++++ ios/Classes/OneSignalOutcomeEventsController.m | 6 +++--- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 8d29afc5..80f4a880 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,7 +34,7 @@ android { } dependencies { - api 'com.onesignal:OneSignal:3.12.1' + api 'com.onesignal:OneSignal:3.12.2' } // Adds required manifestPlaceholders keys to allow mainifest merge gradle step to complete diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java index 9390f5ed..bc454db2 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -1,8 +1,11 @@ package com.onesignal.flutter; +import androidx.annotation.Nullable; + import com.onesignal.OneSignal; import com.onesignal.OutcomeEvent; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import io.flutter.plugin.common.MethodCall; @@ -27,12 +30,16 @@ class OSFlutterOutcomeEventsHandler extends FlutterRegistrarResponder implements } @Override - public void onSuccess(OutcomeEvent outcomeEvent) { + public void onSuccess(@Nullable OutcomeEvent outcomeEvent) { if (this.replySubmitted.getAndSet(true)) return; - replySuccess(result, OneSignalSerializer.convertOutcomeEventToMap(outcomeEvent)); + if (outcomeEvent == null) + replySuccess(result, new HashMap<>()); + else + replySuccess(result, OneSignalSerializer.convertOutcomeEventToMap(outcomeEvent)); } + } public class OneSignalOutcomeEventsController extends FlutterRegistrarResponder implements MethodCallHandler { diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java index ee9f9405..d20b1ee8 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java @@ -201,7 +201,12 @@ static HashMap convertOutcomeEventToMap(OutcomeEvent outcomeEven HashMap hash = new HashMap<>(); hash.put("session", outcomeEvent.getSession().toString()); - hash.put("notification_ids", outcomeEvent.getNotificationIds().toString()); + + if (outcomeEvent.getNotificationIds() == null) + hash.put("notification_ids", new JSONArray().toString()); + else + hash.put("notification_ids", outcomeEvent.getNotificationIds().toString()); + hash.put("id", outcomeEvent.getName()); hash.put("timestamp", outcomeEvent.getTimestamp()); hash.put("weight", String.valueOf(outcomeEvent.getWeight())); diff --git a/example/lib/main.dart b/example/lib/main.dart index f6ddc2f3..b27dd9d9 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -269,6 +269,9 @@ class _MyAppState extends State { } oneSignalOutcomeEventsExamples() async { + // Await example for sending outcomes + outcomeAwaitExample(); + // Send a normal outcome and get a reply with the name of the outcome OneSignal.shared.sendOutcome("normal_1"); OneSignal.shared.sendOutcome("normal_2").then((outcomeEvent) { @@ -291,6 +294,11 @@ class _MyAppState extends State { }); } + Future outcomeAwaitExample() async { + var outcomeEvent = await OneSignal.shared.sendOutcome("await_normal_1"); + print(outcomeEvent.jsonRepresentation()); + } + @override Widget build(BuildContext context) { return new MaterialApp( diff --git a/ios/Classes/OneSignalOutcomeEventsController.m b/ios/Classes/OneSignalOutcomeEventsController.m index 0416b8e9..c86092f7 100644 --- a/ios/Classes/OneSignalOutcomeEventsController.m +++ b/ios/Classes/OneSignalOutcomeEventsController.m @@ -54,20 +54,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result - (void)sendOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments; - [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcome() not implemented for iOS yet!"]; + [OneSignal onesignal_Log:ONE_S_LL_WARN message:@"Method sendOutcome() not implemented for iOS yet!"]; result(@{}); } - (void)sendUniqueOutcome:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments; - [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendUniqueOutcome() not implemented for iOS yet!"]; + [OneSignal onesignal_Log:ONE_S_LL_WARN message:@"Method sendUniqueOutcome() not implemented for iOS yet!"]; result(@{}); } - (void)sendOutcomeWithValue:(FlutterMethodCall *)call withResult:(FlutterResult)result { // NSString *name = call.arguments[@"outcome_name"]; // NSNumber *value = call.arguments[@"outcome_value"]; - [OneSignal onesignal_Log:ONE_S_LL_VERBOSE message:@"Method sendOutcomeWithValue() not implemented for iOS yet!"]; + [OneSignal onesignal_Log:ONE_S_LL_WARN message:@"Method sendOutcomeWithValue() not implemented for iOS yet!"]; result(@{}); } From e7a4301a87aa25092973d0d2978323caa7a315c7 Mon Sep 17 00:00:00 2001 From: mikechoch Date: Thu, 24 Oct 2019 17:24:36 -0700 Subject: [PATCH 9/9] Removed androidx Nullable to prevent conflicts --- .../onesignal/flutter/OneSignalOutcomeEventsController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java index bc454db2..997bb3c5 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -1,7 +1,5 @@ package com.onesignal.flutter; -import androidx.annotation.Nullable; - import com.onesignal.OneSignal; import com.onesignal.OutcomeEvent; @@ -30,7 +28,7 @@ class OSFlutterOutcomeEventsHandler extends FlutterRegistrarResponder implements } @Override - public void onSuccess(@Nullable OutcomeEvent outcomeEvent) { + public void onSuccess(OutcomeEvent outcomeEvent) { if (this.replySubmitted.getAndSet(true)) return;