diff --git a/.gitignore b/.gitignore index e4498c7..282d3e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ example/ios/Podfile.lock -example/pubspec.lock \ No newline at end of file +example/pubspec.lock +.dart_tool/ +build/ +pubspec.lock \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index f16601e..d5d7c78 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -42,7 +42,10 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.iterable:iterableapi:3.4.9' + implementation 'com.iterable:iterableapi:3.4.15' + implementation 'com.iterable:iterableapi-ui:3.4.0' + // Version 17.4.0+ is required for push notifications and in-app message features: + implementation 'com.google.firebase:firebase-messaging:17.4.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20210307' } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 34532b9..29dc62b 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,4 @@ + diff --git a/android/src/main/kotlin/com/lahaus/iterable_flutter/IterableFlutterPlugin.kt b/android/src/main/kotlin/com/lahaus/iterable_flutter/IterableFlutterPlugin.kt index 5ab42e6..0337d2d 100644 --- a/android/src/main/kotlin/com/lahaus/iterable_flutter/IterableFlutterPlugin.kt +++ b/android/src/main/kotlin/com/lahaus/iterable_flutter/IterableFlutterPlugin.kt @@ -15,6 +15,8 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import org.json.JSONObject import java.util.* +import java.net.URL + /** IterableFlutterPlugin */ @@ -52,9 +54,9 @@ class IterableFlutterPlugin : FlutterPlugin, MethodCallHandler { result.success(null) } "setEmail" -> { - val userEmail = call.arguments as String - IterableApi.getInstance().setEmail(userEmail) - IterableApi.getInstance().registerForPush() + val email = call.argument("email") ?: "" + val jwt = call.argument("jwt") ?: "" + IterableApi.getInstance().setEmail(email, jwt) result.success(null) } "setUserId" -> { @@ -93,6 +95,14 @@ class IterableFlutterPlugin : FlutterPlugin, MethodCallHandler { IterableApi.getInstance().updateUser(JSONObject(userInfo)) result.success(null) } + "handleDeepLink" -> { + val argumentData = call.arguments as? Map<*, *> + val url = argumentData?.get("url") as String + IterableApi.getInstance().getAndTrackDeepLink(url) { result:String? -> + Log.d("HandleDeeplink", "Redirected to: $result") + channel.invokeMethod("deepLinkHandler", mapOf("path" to URL(result).path)) + } + } else -> { result.notImplemented() } @@ -107,11 +117,10 @@ class IterableFlutterPlugin : FlutterPlugin, MethodCallHandler { notifyPushNotificationOpened() false } - + if (activeLogDebug) { - configBuilder.setLogLevel(Log.DEBUG) + configBuilder.setLogLevel(Log.VERBOSE) } - IterableApi.initialize(context, apiKey, configBuilder.build()) } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 1ecbeb6..ba25d6c 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -29,7 +29,7 @@ apply from: project(':flutter_config').projectDir.getPath() + "/dotenv.gradle" apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -39,7 +39,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.lahaus.iterable_flutter_example" minSdkVersion 16 - targetSdkVersion 31 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/ios/Classes/SwiftIterableFlutterPlugin.swift b/ios/Classes/SwiftIterableFlutterPlugin.swift index c08907e..8e3161d 100644 --- a/ios/Classes/SwiftIterableFlutterPlugin.swift +++ b/ios/Classes/SwiftIterableFlutterPlugin.swift @@ -3,17 +3,34 @@ import UIKit import IterableSDK import UserNotifications -public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotificationCenterDelegate, IterableCustomActionDelegate, IterableURLDelegate { +public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotificationCenterDelegate, IterableCustomActionDelegate, IterableURLDelegate, IterableAuthDelegate { static var channel: FlutterMethodChannel? = nil + var token: String? = nil public static func register(with registrar: FlutterPluginRegistrar) { + NSLog("calling channel register") channel = FlutterMethodChannel(name: "iterable_flutter", binaryMessenger: registrar.messenger()) let instance = SwiftIterableFlutterPlugin() registrar.addMethodCallDelegate(instance, channel: channel!) registrar.addApplicationDelegate(instance) } + + public func onAuthTokenRequested(completion: @escaping AuthTokenRetrievalHandler) { + print("calling onAuthTokenRequested") + print(token) + completion(token) + } + + public func onAuthFailure(_ authFailure: AuthFailure) { + + } + + public func onTokenRegistrationFailed(_ reason: String?) { + print("token registration failed") + print(reason) + } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch (call.method) { @@ -22,14 +39,17 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica let apiKey = args["apiKey"] as! String let pushIntegrationName = args["pushIntegrationName"] as! String - initialize(apiKey, pushIntegrationName) result(nil) case "setEmail": - let email = call.arguments as! String - IterableAPI.email = email - + let args = getPropertiesFromArguments(call.arguments) + let email = args["email"] as! String + let jwt = args["jwt"] as! String + print("calling IterableAPI.setEmail") + token = jwt + IterableAPI.setEmail(email, jwt) + print("finished calling IterableAPI.setEmail") result(nil) case "setUserId": let userId = call.arguments as! String @@ -67,6 +87,14 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica case "signOut": signOut() + result(nil) + case "handleDeepLink": + NSLog("NSLog handleDeepLink") + let args = getPropertiesFromArguments(call.arguments) + let urlStr = args["url"] as! String + NSLog("deeplink \(urlStr)") + let url = URL(string: urlStr) + IterableAPI.handle(universalLink: url!) result(nil) default: result(FlutterMethodNotImplemented) @@ -76,10 +104,11 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica private func initialize(_ apiKey: String, _ pushIntegrationName: String){ let config = IterableConfig() config.pushIntegrationName = pushIntegrationName - config.autoPushRegistration = true + config.autoPushRegistration = false config.customActionDelegate = self config.urlDelegate = self - + config.authDelegate = self + config.logDelegate = AllLogDelegate() IterableAPI.initialize(apiKey: apiKey, config: config) } @@ -95,12 +124,14 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica } public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable : Any] = [:]) -> Bool { + print("application") UNUserNotificationCenter.current().delegate = self return true } - + public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + print("~~~~~ calling IterableAPI.register") IterableAPI.register(token: deviceToken) } @@ -132,7 +163,15 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica public func handle(iterableURL url: URL, inContext context: IterableActionContext) -> Bool { - notifyPushNotificationOpened() + NSLog("deeplink: handle(iterableURL url: URL,") + NSLog("deeplink \(url.path)") + let payload = [ + "path": url.path ?? "", + "query": url.query ?? "" + ] as [String : Any] + NSLog("calling deepLinkHandler up the channel") + NSLog("is there a channel? \(SwiftIterableFlutterPlugin.channel == nil)") + SwiftIterableFlutterPlugin.channel?.invokeMethod("deepLinkHandler", arguments: payload) return true } @@ -151,7 +190,5 @@ public class SwiftIterableFlutterPlugin: NSObject, FlutterPlugin, UNUserNotifica ] as [String : Any] SwiftIterableFlutterPlugin.channel?.invokeMethod("openedNotificationHandler", arguments: payload) - } - } diff --git a/ios/iterable_flutter.podspec b/ios/iterable_flutter.podspec index 5a559a4..1f31d1c 100644 --- a/ios/iterable_flutter.podspec +++ b/ios/iterable_flutter.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.dependency 'Iterable-iOS-SDK', '6.4.7' + s.dependency 'Iterable-iOS-SDK', '6.5.7' s.platform = :ios, '11.0' # Flutter.framework does not contain a i386 slice. diff --git a/lib/iterable_flutter.dart b/lib/iterable_flutter.dart index 09d0386..79a63aa 100644 --- a/lib/iterable_flutter.dart +++ b/lib/iterable_flutter.dart @@ -4,17 +4,19 @@ import 'dart:convert'; import 'package:flutter/services.dart'; typedef OpenedNotificationHandler = void Function(Map openedResult); +typedef OpenedDeepLinkHandler = void Function(Map openedResult); // ignore: avoid_classes_with_only_static_members class IterableFlutter { static const MethodChannel _channel = MethodChannel('iterable_flutter'); static OpenedNotificationHandler? _onOpenedNotification; + static OpenedDeepLinkHandler? _onOpenedDeepLink; static Future initialize({ required String apiKey, required String pushIntegrationName, - bool activeLogDebug = false, + bool activeLogDebug = false }) async { await _channel.invokeMethod( 'initialize', @@ -27,8 +29,20 @@ class IterableFlutter { _channel.setMethodCallHandler(nativeMethodCallHandler); } - static Future setEmail(String email) async { - await _channel.invokeMethod('setEmail', email); + static Future setEmail(String email, String jwt) async { + await _channel.invokeMethod( + 'setEmail', + { + 'email': email, + 'jwt': jwt, + }, + ); + } + + static Future handleDeepLink(url) async { + print("handleDeepLink"); + print(url); + await _channel.invokeMethod("handleDeepLink", {'url': url}); } static Future setUserId(String userId) async { @@ -69,14 +83,23 @@ class IterableFlutter { _onOpenedNotification = handler; } + static void setDeepLinkOpenedHandler(OpenedDeepLinkHandler handler) { + print("calling setDeepLinkOpenedHandler "); + _onOpenedDeepLink = handler; + } + static Future nativeMethodCallHandler(MethodCall methodCall) async { + print("nativeMethodCallHandler maybe deep? ${methodCall.method}"); final arguments = methodCall.arguments as Map; final argumentsCleaned = sanitizeArguments(arguments); - switch (methodCall.method) { case "openedNotificationHandler": _onOpenedNotification?.call(argumentsCleaned); return "This data from native....."; + case "deepLinkHandler": + print("deepLinkHandler running... "); + _onOpenedDeepLink?.call(argumentsCleaned); + return "Deep link datta from handler..."; default: return "Nothing"; } @@ -86,7 +109,7 @@ class IterableFlutter { Map arguments) { final result = arguments; - final data = result['additionalData']; + final data = result['additionalData'] ?? {}; data.forEach((key, value) { if (value is String) { if (value[0] == '{' && value[value.length - 1] == '}') { diff --git a/pubspec.yaml b/pubspec.yaml index c086c15..b20252b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: iterable_flutter description: Flutter implementation for iterable.com Cross Channel Marketing Platform -version: 0.5.8 +version: 0.9.3 homepage: https://lahaus.com repository: https://github.com/la-haus/iterable-flutter issue_tracker: https://github.com/la-haus/iterable-flutter/issues diff --git a/test/iterable_flutter_test.dart b/test/iterable_flutter_test.dart index 75f4a23..5f22591 100644 --- a/test/iterable_flutter_test.dart +++ b/test/iterable_flutter_test.dart @@ -13,7 +13,9 @@ void main() { const String activeLogDebug = 'activeLogDebug'; const String email = 'my@email.com'; const String userId = '11111'; + const String jwt = ''; const String event = 'my_event'; + const String authToken = 'authToken'; const Map dataFields = {'data': 'field'}; const contentBody = {'testKey': "Test body push"}; @@ -64,16 +66,20 @@ void main() { arguments: { apiKey: apiKey, pushIntegrationName: pushIntegrationName, - activeLogDebug: false + activeLogDebug: false, + authToken: null }, ), ]); }); test('setEmail', () async { - await IterableFlutter.setEmail(email); + await IterableFlutter.setEmail(email, jwt); expect(calledMethod, [ - isMethodCall('setEmail', arguments: email), + isMethodCall('setEmail', arguments: { + "email": email, + "jwt": jwt + }), ]); });