diff --git a/android/build.gradle b/android/build.gradle index a7ecf127..80f4a880 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.2' } // 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..997bb3c5 --- /dev/null +++ b/android/src/main/java/com/onesignal/flutter/OneSignalOutcomeEventsController.java @@ -0,0 +1,106 @@ +package com.onesignal.flutter; + +import com.onesignal.OneSignal; +import com.onesignal.OutcomeEvent; + +import java.util.HashMap; +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 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); + + OSFlutterOutcomeEventsHandler(PluginRegistry.Registrar flutterRegistrar, MethodChannel channel, Result result) { + this.flutterRegistrar = flutterRegistrar; + this.channel = channel; + this.result = result; + } + + @Override + public void onSuccess(OutcomeEvent outcomeEvent) { + if (this.replySubmitted.getAndSet(true)) + return; + + if (outcomeEvent == null) + replySuccess(result, new HashMap<>()); + else + replySuccess(result, OneSignalSerializer.convertOutcomeEventToMap(outcomeEvent)); + } + +} + +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()) { + replyError(result, "OneSignal", "sendOutcome() name must not be null or empty", 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()) { + replyError(result, "OneSignal", "sendUniqueOutcome() name must not be null or empty", 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()) { + replyError(result, "OneSignal", "sendOutcomeWithValue() name must not be null or empty", null); + return; + } + + if (value == null) { + replyError(result, "OneSignal", "sendOutcomeWithValue() value must not be null", 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/OneSignalSerializer.java b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java index ebab687a..d20b1ee8 100644 --- a/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java +++ b/android/src/main/java/com/onesignal/flutter/OneSignalSerializer.java @@ -197,6 +197,23 @@ static HashMap convertInAppMessageClickedActionToMap(OSInAppMess return hash; } + static HashMap convertOutcomeEventToMap(OutcomeEvent outcomeEvent) { + HashMap hash = new HashMap<>(); + + hash.put("session", outcomeEvent.getSession().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())); + + return hash; + } + private static HashMap convertAndroidBackgroundImageLayoutToMap(BackgroundImageLayout layout) { HashMap hash = new HashMap<>(); 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..b27dd9d9 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,37 @@ class _MyAppState extends State { OneSignal.shared.pauseInAppMessages(false); } + 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) { + 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_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); + OneSignal.shared.sendOutcomeWithValue("value_2", 3.9).then((outcomeEvent) { + var json = outcomeEvent.jsonRepresentation(); + print("Successfully sent outcome event with value: $json"); + }); + } + + 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/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/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/OneSignalInAppMessagesController.m b/ios/Classes/OneSignalInAppMessagesController.m index eb2c60fa..0872e725 100644 --- a/ios/Classes/OneSignalInAppMessagesController.m +++ b/ios/Classes/OneSignalInAppMessagesController.m @@ -61,26 +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.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..c86092f7 --- /dev/null +++ b/ios/Classes/OneSignalOutcomeEventsController.m @@ -0,0 +1,74 @@ +/** + * 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 { + OneSignalOutcomeEventsController *instance = [OneSignalOutcomeEventsController 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 withResult:(FlutterResult)result { +// NSString *name = call.arguments; + [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_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_WARN message:@"Method sendOutcomeWithValue() not implemented for iOS yet!"]; + result(@{}); +} + +@end diff --git a/ios/Classes/OneSignalPlugin.m b/ios/Classes/OneSignalPlugin.m index dd2906ab..ee8857ef 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 { @@ -167,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 { @@ -194,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 { @@ -212,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 { @@ -225,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 { @@ -243,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); }]; @@ -251,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); }]; @@ -262,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/lib/onesignal_flutter.dart b/lib/onesignal_flutter.dart index 717b3a61..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); @@ -35,6 +37,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 +318,27 @@ 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 { + 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 { + 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 { + 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 Future _handleMethod(MethodCall call) async { if (call.method == 'OneSignal#handleReceivedNotification' && diff --git a/lib/src/defines.dart b/lib/src/defines.dart index 8392c276..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 } @@ -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..b6ccc6b4 --- /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["id"] == null ? + "" : + outcome["id"] 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, + 'id': 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; } 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