From 190d6fcf06802551cb2ef4c7b5d7badefc949ec7 Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Mon, 20 Sep 2021 13:31:10 +0300 Subject: [PATCH 1/8] Reformat code with dartfmt --- lib/src/constants.dart | 3 ++- lib/src/models/product.dart | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 7b51dac..6a28e78 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -40,7 +40,8 @@ class Constants { static const mStoreSdkInfo = 'storeSdkInfo'; static const mIdentify = 'identify'; static const mLogout = 'logout'; - static const mSetAppleSearchAdsAttributionEnabled = 'setAppleSearchAdsAttributionEnabled'; + static const mSetAppleSearchAdsAttributionEnabled = + 'setAppleSearchAdsAttributionEnabled'; // Keys for NSUserDefaults on iOS and SharedPreferences on Android static const keyPrefix = 'com.qonversion.keys'; diff --git a/lib/src/models/product.dart b/lib/src/models/product.dart index ff5766e..1d712e7 100644 --- a/lib/src/models/product.dart +++ b/lib/src/models/product.dart @@ -121,7 +121,7 @@ class QProduct { final skuDetails = this.skuDetails; final skProduct = this.skProduct; - if(skuDetails != null) { + if (skuDetails != null) { storeTitle = skuDetails.title; storeDescription = skuDetails.description; currencyCode = skuDetails.priceCurrencyCode; @@ -134,8 +134,7 @@ class QProduct { } else { prettyIntroductoryPrice = introPrice; } - } - else if(skProduct != null) { + } else if (skProduct != null) { storeTitle = skProduct.localizedTitle; storeDescription = skProduct.localizedDescription; currencyCode = skProduct.priceLocale?.currencyCode; From ba228795bdca97380d14fca59735d8844b99d984 Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Wed, 24 Nov 2021 14:10:58 +0300 Subject: [PATCH 2/8] Update native android version to 3.2.2 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 524ac95..ceb6b88 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,7 +3,7 @@ version '4.1.1' buildscript { ext.kotlin_version = '1.3.50' - ext.qonversion_version = '3.1.1' + ext.qonversion_version = '3.2.2' repositories { google() jcenter() From d2a5d32aeef42e204963e96510adfea0624a50c7 Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Wed, 24 Nov 2021 16:11:01 +0300 Subject: [PATCH 3/8] Add Automations logic --- .../AutomationsPlugin.kt | 72 +++++++++++++++++++ .../sdk/qonversion_flutter_sdk/Mapper.kt | 49 +++++++++++++ .../QonversionFlutterSdkPlugin.kt | 31 +++++++- example/lib/constants.dart | 5 ++ ios/Classes/AutomationsPlugin.swift | 66 +++++++++++++++++ ios/Classes/Mapper.swift | 41 ++++++++++- .../SwiftQonversionFlutterSdkPlugin.swift | 32 ++++++++- lib/qonversion_flutter.dart | 18 +++-- lib/src/automations.dart | 64 +++++++++++++++++ lib/src/constants.dart | 4 ++ lib/src/models/automations/action_result.dart | 28 ++++++++ .../models/automations/action_result.g.dart | 72 +++++++++++++++++++ .../automations/action_result_type.dart | 18 +++++ lib/src/models/automations/event.dart | 28 ++++++++ lib/src/models/automations/event.g.dart | 67 +++++++++++++++++ lib/src/models/automations/event_type.dart | 38 ++++++++++ lib/src/models/launch_result.dart | 2 +- lib/src/models/launch_result.g.dart | 2 +- lib/src/models/permission.dart | 4 +- lib/src/models/permission.g.dart | 6 +- lib/src/models/product.g.dart | 2 +- lib/src/models/qonversion_error.dart | 30 ++++++++ lib/src/models/qonversion_error.g.dart | 21 ++++++ lib/src/models/utils/mapper.dart | 20 +++++- lib/src/qonversion.dart | 31 +++++++- pubspec.lock | 56 +++++++-------- 26 files changed, 759 insertions(+), 48 deletions(-) create mode 100644 android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt create mode 100644 example/lib/constants.dart create mode 100644 ios/Classes/AutomationsPlugin.swift create mode 100644 lib/src/automations.dart create mode 100644 lib/src/models/automations/action_result.dart create mode 100644 lib/src/models/automations/action_result.g.dart create mode 100644 lib/src/models/automations/action_result_type.dart create mode 100644 lib/src/models/automations/event.dart create mode 100644 lib/src/models/automations/event.g.dart create mode 100644 lib/src/models/automations/event_type.dart create mode 100644 lib/src/models/qonversion_error.dart create mode 100644 lib/src/models/qonversion_error.g.dart diff --git a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt new file mode 100644 index 0000000..801ca64 --- /dev/null +++ b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt @@ -0,0 +1,72 @@ +package com.qonversion.flutter.sdk.qonversion_flutter_sdk + +import com.google.gson.Gson +import com.qonversion.android.sdk.automations.Automations +import com.qonversion.android.sdk.automations.AutomationsDelegate +import com.qonversion.android.sdk.automations.QActionResult +import io.flutter.plugin.common.BinaryMessenger + +class AutomationsPlugin { + private var shownScreensStreamHandler: BaseEventStreamHandler? = null + private var startedActionsStreamHandler: BaseEventStreamHandler? = null + private var failedActionsStreamHandler: BaseEventStreamHandler? = null + private var finishedActionsStreamHandler: BaseEventStreamHandler? = null + private var finishedAutomationsStreamHandler: BaseEventStreamHandler? = null + + companion object { + const val EVENT_CHANNEL_SHOWN_SCREENS = "shown_screens" + const val EVENT_CHANNEL_STARTED_ACTIONS = "started_actions" + const val EVENT_CHANNEL_FAILED_ACTIONS = "failed_actions" + const val EVENT_CHANNEL_FINISHED_ACTIONS = "finished_actions" + const val EVENT_CHANNEL_FINISHED_AUTOMATIONS = "finished_automations" + } + + fun register(messenger: BinaryMessenger) { + val shownScreensListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_SHOWN_SCREENS) + shownScreensListener.register() + shownScreensStreamHandler = shownScreensListener.eventStreamHandler + + val startedActionsListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_STARTED_ACTIONS) + startedActionsListener.register() + startedActionsStreamHandler = startedActionsListener.eventStreamHandler + + val failedActionsListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_FAILED_ACTIONS) + failedActionsListener.register() + failedActionsStreamHandler = failedActionsListener.eventStreamHandler + + val finishedActionsListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_FINISHED_ACTIONS) + finishedActionsListener.register() + finishedActionsStreamHandler = finishedActionsListener.eventStreamHandler + + val finishedAutomationsListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_FINISHED_AUTOMATIONS) + finishedAutomationsListener.register() + finishedAutomationsStreamHandler = finishedAutomationsListener.eventStreamHandler + } + + fun setAutomationsDelegate() { + Automations.setDelegate(object : AutomationsDelegate { + override fun automationsDidShowScreen(screenId: String) { + shownScreensStreamHandler?.eventSink?.success(screenId) + } + + override fun automationsDidStartExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + startedActionsStreamHandler?.eventSink?.success(payload) + } + + override fun automationsDidFailExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + failedActionsStreamHandler?.eventSink?.success(payload) + } + + override fun automationsDidFinishExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + finishedActionsStreamHandler?.eventSink?.success(payload) + } + + override fun automationsFinished() { + finishedAutomationsStreamHandler?.eventSink?.success(null) + } + }) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/Mapper.kt b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/Mapper.kt index 3bcd241..4663a3e 100644 --- a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/Mapper.kt +++ b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/Mapper.kt @@ -6,6 +6,10 @@ import com.google.gson.JsonSyntaxException import com.google.gson.reflect.TypeToken import com.qonversion.android.sdk.QonversionError import com.qonversion.android.sdk.QonversionErrorCode +import com.qonversion.android.sdk.automations.AutomationsEvent +import com.qonversion.android.sdk.automations.AutomationsEventType +import com.qonversion.android.sdk.automations.QActionResult +import com.qonversion.android.sdk.automations.QActionResultType import com.qonversion.android.sdk.dto.QLaunchResult import com.qonversion.android.sdk.dto.QPermission import com.qonversion.android.sdk.dto.eligibility.QEligibility @@ -151,3 +155,48 @@ fun mapQProduct(jsonProduct: String): QProduct? { it.trialDuration = productTrialDuration } } + +fun QActionResult.toMap(): Map { + return mapOf("action_type" to type.toInt(), + "parameters" to value, + "error" to error.toMap()) +} + +fun QActionResultType.toInt(): Int { + return when (this) { + QActionResultType.Unknown -> 0 + QActionResultType.Url -> 1 + QActionResultType.DeepLink -> 2 + QActionResultType.Navigation -> 3 + QActionResultType.Purchase -> 4 + QActionResultType.Restore -> 5 + QActionResultType.Close -> 6 + } +} + +fun AutomationsEvent.toMap(): Map { + return mapOf("event_type" to type.toInt(), + "date" to date.time.toDouble()) +} + +fun AutomationsEventType.toInt(): Int { + return when (this) { + AutomationsEventType.Unknown -> 0 + AutomationsEventType.TrialStarted -> 1 + AutomationsEventType.TrialConverted -> 2 + AutomationsEventType.TrialCanceled -> 3 + AutomationsEventType.TrialBillingRetry -> 4 + AutomationsEventType.SubscriptionStarted -> 5 + AutomationsEventType.SubscriptionRenewed -> 6 + AutomationsEventType.SubscriptionRefunded -> 7 + AutomationsEventType.SubscriptionCanceled -> 8 + AutomationsEventType.SubscriptionBillingRetry -> 9 + AutomationsEventType.InAppPurchase -> 10 + AutomationsEventType.SubscriptionUpgraded -> 11 + AutomationsEventType.TrialStillActive -> 12 + AutomationsEventType.TrialExpired -> 13 + AutomationsEventType.SubscriptionExpired -> 14 + AutomationsEventType.SubscriptionDowngraded -> 15 + AutomationsEventType.SubscriptionProductChanged -> 16 + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionFlutterSdkPlugin.kt b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionFlutterSdkPlugin.kt index 4a10759..50d54ec 100644 --- a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionFlutterSdkPlugin.kt @@ -2,9 +2,9 @@ package com.qonversion.flutter.sdk.qonversion_flutter_sdk import android.app.Activity import android.app.Application +import android.preference.PreferenceManager import android.util.Log import androidx.annotation.NonNull -import androidx.preference.PreferenceManager import com.google.gson.Gson import com.google.gson.JsonSyntaxException import com.qonversion.android.sdk.* @@ -30,6 +30,7 @@ class QonversionFlutterSdkPlugin : MethodCallHandler, FlutterPlugin, ActivityAwa private var application: Application? = null private var channel: MethodChannel? = null private var deferredPurchasesStreamHandler: BaseEventStreamHandler? = null + private var automationsPlugin: AutomationsPlugin? = null companion object { const val METHOD_CHANNEL = "qonversion_flutter_sdk" @@ -115,6 +116,8 @@ class QonversionFlutterSdkPlugin : MethodCallHandler, FlutterPlugin, ActivityAwa "checkTrialIntroEligibility" -> checkTrialIntroEligibility(args, result) "storeSdkInfo" -> storeSdkInfo(args, result) "identify" -> identify(args["userId"] as? String, result) + "setNotificationsToken" -> setNotificationsToken(args["notificationsToken"] as? String, result) + "handleNotification" -> handleNotification(args, result) else -> result.notImplemented() } } @@ -138,7 +141,9 @@ class QonversionFlutterSdkPlugin : MethodCallHandler, FlutterPlugin, ActivityAwa } ) startListeningPendingPurchasesEvents() - } ?: result.error(QonversionErrorCode.UnknownError.name, "Couldn't launch Qonversion. There is no Application context", null) + automationsPlugin?.setAutomationsDelegate() + } + ?: result.error(QonversionErrorCode.UnknownError.name, "Couldn't launch Qonversion. There is no Application context", null) } private fun identify(userId: String?, result: Result) { @@ -347,6 +352,25 @@ class QonversionFlutterSdkPlugin : MethodCallHandler, FlutterPlugin, ActivityAwa }) } + private fun setNotificationsToken(token: String?, result: Result) { + token?.let { + Qonversion.setNotificationsToken(it) + result.success(null) + } ?: result.noArgsError() + } + + private fun handleNotification(args: Map, result: Result) { + val data = args["notificationData"] as? Map ?: return result.noDataError() + + if (data.isEmpty()) { + return result.noDataError() + } + + val stringsMap: Map = data.mapValues { it.value.toString() } + val isQonversionNotification = Qonversion.handleNotification(stringsMap) + result.success(isQonversionNotification) + } + private fun getPurchasesListener(result: Result) = object : QonversionPermissionsCallback { override fun onSuccess(permissions: Map) = result.success(PurchaseResult(permissions).toMap()) @@ -414,6 +438,9 @@ class QonversionFlutterSdkPlugin : MethodCallHandler, FlutterPlugin, ActivityAwa // Register promo purchases events. Android SDK does not generate any promo purchases yet val promoPurchasesListener = BaseListenerWrapper(messenger, EVENT_CHANNEL_PROMO_PURCHASES) promoPurchasesListener.register() + automationsPlugin = AutomationsPlugin().apply { + register(messenger) + } } private fun tearDown() { diff --git a/example/lib/constants.dart b/example/lib/constants.dart new file mode 100644 index 0000000..9c1759d --- /dev/null +++ b/example/lib/constants.dart @@ -0,0 +1,5 @@ +class Constants { + // Firebase + static const channelId = 'high_importance_channel'; + static const channelName = 'High Importance Notifications'; +} diff --git a/ios/Classes/AutomationsPlugin.swift b/ios/Classes/AutomationsPlugin.swift new file mode 100644 index 0000000..6c8cc38 --- /dev/null +++ b/ios/Classes/AutomationsPlugin.swift @@ -0,0 +1,66 @@ +// +// AutomationsPlugin.swift +// qonversion_flutter +// +// Created by Maria on 18.11.2021. +// + +import Qonversion + +public class AutomationsPlugin: NSObject { + private let eventChannelShownScreens = "shown_screens" + private let eventChannelStartedActions = "started_actions" + private let eventChannelFailedActions = "failed_actions" + private let eventChannelFinishedActions = "finished_actions" + private let eventChannelFinishedAutomations = "finished_automations" + + private var shownScreensStreamHandler: BaseEventStreamHandler? + private var startedActionsStreamHandler: BaseEventStreamHandler? + private var failedActionsStreamHandler: BaseEventStreamHandler? + private var finishedActionsStreamHandler: BaseEventStreamHandler? + private var finishedAutomationsStreamHandler: BaseEventStreamHandler? + + public func register(_ registrar: FlutterPluginRegistrar) { + let shownScreensListener = FlutterListenerWrapper(registrar, postfix: eventChannelShownScreens) + shownScreensListener.register() { self.shownScreensStreamHandler = $0 } + + let startedActionsListener = FlutterListenerWrapper(registrar, postfix: eventChannelStartedActions) + startedActionsListener.register() { self.startedActionsStreamHandler = $0 } + + let failedActionsListener = FlutterListenerWrapper(registrar, postfix: eventChannelFailedActions) + failedActionsListener.register() { self.failedActionsStreamHandler = $0 } + + let finishedActionsListener = FlutterListenerWrapper(registrar, postfix: eventChannelFinishedActions) + finishedActionsListener.register() { self.finishedActionsStreamHandler = $0 } + + let finishedAutomationsListener = FlutterListenerWrapper(registrar, postfix: eventChannelFinishedAutomations) + finishedAutomationsListener.register() { self.finishedAutomationsStreamHandler = $0 } + + Qonversion.Automations.setDelegate(self) + } +} + +extension AutomationsPlugin: Qonversion.AutomationsDelegate { + public func automationsDidShowScreen(_ screenID: String) { + shownScreensStreamHandler?.eventSink?(screenID) + } + + public func automationsDidStartExecuting(actionResult: Qonversion.ActionResult) { + let payload = actionResult.toMap().toJson() + startedActionsStreamHandler?.eventSink?(payload) + } + + public func automationsDidFailExecuting(actionResult: Qonversion.ActionResult) { + let payload = actionResult.toMap().toJson() + failedActionsStreamHandler?.eventSink?(payload) + } + + public func automationsDidFinishExecuting(actionResult: Qonversion.ActionResult) { + let payload = actionResult.toMap().toJson() + finishedActionsStreamHandler?.eventSink?(payload) + } + + public func automationsFinished() { + finishedAutomationsStreamHandler?.eventSink?(nil) + } +} diff --git a/ios/Classes/Mapper.swift b/ios/Classes/Mapper.swift index f4e49c3..fe699e0 100644 --- a/ios/Classes/Mapper.swift +++ b/ios/Classes/Mapper.swift @@ -23,7 +23,6 @@ struct PurchaseResult { "is_cancelled": isCancelled, ] } - } extension NSError { @@ -37,6 +36,30 @@ extension NSError { } } +extension Date { + func toMilliseconds() -> Double { + return timeIntervalSince1970 * 1000 + } +} + +extension String { + func toData() -> Data { + let len = count / 2 + var data = Data(capacity: len) + var i = startIndex + for _ in 0.. [String: Any] { return [ @@ -168,3 +191,19 @@ extension Dictionary { } } +extension Qonversion.ActionResult { + func toMap() -> [String: Any?] { + let nsError = error as NSError? + + return ["action_type": type.rawValue, + "parameters": parameters, + "error": nsError?.toMap()] + } +} + +extension QONAutomationsEvent { + func toMap() -> [String: Any?] { + return ["event_type": type.rawValue, + "date": date.toMilliseconds()] + } +} diff --git a/ios/Classes/SwiftQonversionFlutterSdkPlugin.swift b/ios/Classes/SwiftQonversionFlutterSdkPlugin.swift index 6a2fdf0..cdede87 100644 --- a/ios/Classes/SwiftQonversionFlutterSdkPlugin.swift +++ b/ios/Classes/SwiftQonversionFlutterSdkPlugin.swift @@ -10,6 +10,8 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin { var deferredPurchasesStreamHandler: BaseEventStreamHandler? var promoPurchasesStreamHandler: BaseEventStreamHandler? var promoPurchasesExecutionBlocks = [String: Qonversion.PromoPurchaseCompletionHandler]() + private var automationsPlugin: AutomationsPlugin? + private var shownScreensStreamHandler: BaseEventStreamHandler? public static func register(with registrar: FlutterPluginRegistrar) { let messenger: FlutterBinaryMessenger @@ -31,6 +33,9 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin { let promoPurchasesListener = FlutterListenerWrapper(registrar, postfix: "promo_purchases") promoPurchasesListener.register() { instance.promoPurchasesStreamHandler = $0 } Qonversion.setPromoPurchasesDelegate(instance) + + instance.automationsPlugin = AutomationsPlugin() + instance.automationsPlugin?.register(registrar) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { @@ -109,7 +114,13 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin { case "setAppleSearchAdsAttributionEnabled": let enable = args["enable"] as? Bool ?? false return setAppleSearchAdsAttributionEnabled(enable, result) - + + case "setNotificationsToken": + return setNotificationsToken(args["notificationsToken"] as? String, result) + + case "handleNotification": + return handleNotification(args, result) + default: return result(FlutterMethodNotImplemented) } @@ -360,6 +371,25 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin { } result(nil) } + + private func setNotificationsToken(_ token: String?, _ result: @escaping FlutterResult) { + guard let token = token else { + result(FlutterError.noArgs) + return + } + let tokenData = token.toData() + Qonversion.setNotificationsToken(tokenData) + result(nil) + } + + private func handleNotification(_ args: [AnyHashable: Any], _ result: @escaping FlutterResult) { + guard let notificationData = args["notificationData"] as? [AnyHashable: Any] else { + return result(FlutterError.noData) + } + + let isPushHandled: Bool = Qonversion.handleNotification(notificationData) + result(isPushHandled) + } } extension SwiftQonversionFlutterSdkPlugin: Qonversion.PurchasesDelegate { diff --git a/lib/qonversion_flutter.dart b/lib/qonversion_flutter.dart index e6903ee..5c9ab82 100644 --- a/lib/qonversion_flutter.dart +++ b/lib/qonversion_flutter.dart @@ -1,16 +1,22 @@ +export 'src/automations.dart'; +export 'src/models/automations/action_result.dart'; +export 'src/models/automations/action_result_type.dart'; +export 'src/models/automations/event.dart'; +export 'src/models/automations/event_type.dart'; export 'src/models/eligibility.dart'; export 'src/models/launch_result.dart'; export 'src/models/offerings.dart'; export 'src/models/permission.dart'; export 'src/models/product.dart'; -export 'src/models/proration_mode.dart'; -export 'src/models/purchase_exception.dart'; -export 'src/models/user_property.dart'; -export 'src/qa_provider.dart'; -export 'src/models/sk_product/discount_payment_mode.dart'; -export 'src/models/sk_product/subscription_period_unit.dart'; export 'src/models/product_duration.dart'; export 'src/models/product_renew_state.dart'; export 'src/models/product_type.dart'; export 'src/models/proration_mode.dart'; +export 'src/models/proration_mode.dart'; +export 'src/models/purchase_exception.dart'; +export 'src/models/qonversion_error.dart'; +export 'src/models/sk_product/discount_payment_mode.dart'; +export 'src/models/sk_product/subscription_period_unit.dart'; +export 'src/models/user_property.dart'; +export 'src/qa_provider.dart'; export 'src/qonversion.dart'; diff --git a/lib/src/automations.dart b/lib/src/automations.dart new file mode 100644 index 0000000..48956aa --- /dev/null +++ b/lib/src/automations.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:qonversion_flutter/qonversion_flutter.dart'; + +class Automations { + static const _shownScreensEventChannel = + EventChannel('qonversion_flutter_shown_screens'); + static const _startedActionsEventChannel = + EventChannel('qonversion_flutter_started_actions'); + static const _failedActionsEventChannel = + EventChannel('qonversion_flutter_failed_actions'); + static const _finishedActionsEventChannel = + EventChannel('qonversion_flutter_finished_actions'); + static const _finishedAutomationsEventChannel = + EventChannel('qonversion_flutter_finished_automations'); + + /// Called when Automations' screen is shown + /// [screenId] shown screen Id + static Stream get shownScreensStream => + _shownScreensEventChannel.receiveBroadcastStream().cast(); + + /// Called when Automations flow starts executing an action + /// [actionResult] action that is being executed + static Stream get startedActionsStream => + _startedActionsEventChannel + .receiveBroadcastStream() + .cast() + .map((event) { + return _handleActionEvent(event); + }); + + /// Called when Automations flow fails executing an action + /// [actionResult] failed action + static Stream get failedActionsStream => + _failedActionsEventChannel + .receiveBroadcastStream() + .cast() + .map((event) { + return _handleActionEvent(event); + }); + + /// Called when Automations flow finishes executing an action + /// [actionResult] executed action. + /// For instance, if the user made a purchase then action.type = ActionResultType.purchase. + /// You can use the Qonversion.checkPermissions() method to get available permissions + static Stream get finishedActionsStream => + _finishedActionsEventChannel + .receiveBroadcastStream() + .cast() + .map((event) { + return _handleActionEvent(event); + }); + + /// Called when Automations flow is finished and the Automations screen is closed + static Stream get finishedAutomationsStream => + _finishedAutomationsEventChannel.receiveBroadcastStream().cast(); + + static ActionResult _handleActionEvent(String event) { + final Map decodedEvent = jsonDecode(event); + + return ActionResult.fromJson(decodedEvent); + } +} diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 6a28e78..e01c31c 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -17,6 +17,8 @@ class Constants { static const kValue = 'value'; static const kProrationMode = 'proration_mode'; static const kEnableAppleSearchAdsAttribution = 'enable'; + static const kNotificationsToken = 'notificationsToken'; + static const kNotificationData = 'notificationData'; // MethodChannel methods names static const mLaunch = 'launch'; @@ -42,6 +44,8 @@ class Constants { static const mLogout = 'logout'; static const mSetAppleSearchAdsAttributionEnabled = 'setAppleSearchAdsAttributionEnabled'; + static const mSetNotificationsToken = 'setNotificationsToken'; + static const mHandleNotification = 'handleNotification'; // Keys for NSUserDefaults on iOS and SharedPreferences on Android static const keyPrefix = 'com.qonversion.keys'; diff --git a/lib/src/models/automations/action_result.dart b/lib/src/models/automations/action_result.dart new file mode 100644 index 0000000..05b8dc3 --- /dev/null +++ b/lib/src/models/automations/action_result.dart @@ -0,0 +1,28 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:qonversion_flutter/src/models/qonversion_error.dart'; +import 'package:qonversion_flutter/src/models/utils/mapper.dart'; +import 'action_result_type.dart'; + +part 'action_result.g.dart'; + +@JsonSerializable() +class ActionResult { + @JsonKey(name: "action_type", defaultValue: ActionResultType.unknown) + final ActionResultType type; + + @JsonKey(name: "parameters") + final Map? parameters; + + @JsonKey( + name: "error", + fromJson: QMapper.qonversionErrorFromJson, + ) + final QError? error; + + const ActionResult(this.type, this.parameters, this.error); + + factory ActionResult.fromJson(Map json) => + _$ActionResultFromJson(json); + + Map toJson() => _$ActionResultToJson(this); +} diff --git a/lib/src/models/automations/action_result.g.dart b/lib/src/models/automations/action_result.g.dart new file mode 100644 index 0000000..68a87a2 --- /dev/null +++ b/lib/src/models/automations/action_result.g.dart @@ -0,0 +1,72 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'action_result.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ActionResult _$ActionResultFromJson(Map json) { + return ActionResult( + _$enumDecodeNullable(_$ActionResultTypeEnumMap, json['action_type']) ?? + ActionResultType.unknown, + (json['parameters'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + QMapper.qonversionErrorFromJson(json['error']), + ); +} + +Map _$ActionResultToJson(ActionResult instance) => + { + 'action_type': _$ActionResultTypeEnumMap[instance.type], + 'parameters': instance.parameters, + 'error': instance.error, + }; + +K _$enumDecode( + Map enumValues, + Object? source, { + K? unknownValue, +}) { + if (source == null) { + throw ArgumentError( + 'A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}', + ); + } + + return enumValues.entries.singleWhere( + (e) => e.value == source, + orElse: () { + if (unknownValue == null) { + throw ArgumentError( + '`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}', + ); + } + return MapEntry(unknownValue, enumValues.values.first); + }, + ).key; +} + +K? _$enumDecodeNullable( + Map enumValues, + dynamic source, { + K? unknownValue, +}) { + if (source == null) { + return null; + } + return _$enumDecode(enumValues, source, unknownValue: unknownValue); +} + +const _$ActionResultTypeEnumMap = { + ActionResultType.unknown: 0, + ActionResultType.url: 1, + ActionResultType.deepLink: 2, + ActionResultType.navigation: 3, + ActionResultType.purchase: 4, + ActionResultType.restore: 5, + ActionResultType.close: 6, +}; diff --git a/lib/src/models/automations/action_result_type.dart b/lib/src/models/automations/action_result_type.dart new file mode 100644 index 0000000..96f3a13 --- /dev/null +++ b/lib/src/models/automations/action_result_type.dart @@ -0,0 +1,18 @@ +import 'package:json_annotation/json_annotation.dart'; + +enum ActionResultType { + @JsonValue(0) + unknown, + @JsonValue(1) + url, + @JsonValue(2) + deepLink, + @JsonValue(3) + navigation, + @JsonValue(4) + purchase, + @JsonValue(5) + restore, + @JsonValue(6) + close, +} diff --git a/lib/src/models/automations/event.dart b/lib/src/models/automations/event.dart new file mode 100644 index 0000000..441c5b1 --- /dev/null +++ b/lib/src/models/automations/event.dart @@ -0,0 +1,28 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:qonversion_flutter/src/models/utils/mapper.dart'; + +import 'event_type.dart'; + +part 'event.g.dart'; + +@JsonSerializable() +class AutomationsEvent { + @JsonKey( + name: 'event_type', + unknownEnumValue: AutomationsEventType.unknown, + ) + final AutomationsEventType type; + + @JsonKey( + name: 'date', + fromJson: QMapper.dateTimeFromSecondsTimestamp, + ) + final DateTime date; + + const AutomationsEvent(this.type, this.date); + + factory AutomationsEvent.fromJson(Map json) => + _$AutomationsEventFromJson(json); + + Map toJson() => _$AutomationsEventToJson(this); +} diff --git a/lib/src/models/automations/event.g.dart b/lib/src/models/automations/event.g.dart new file mode 100644 index 0000000..ce9af00 --- /dev/null +++ b/lib/src/models/automations/event.g.dart @@ -0,0 +1,67 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'event.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +AutomationsEvent _$AutomationsEventFromJson(Map json) { + return AutomationsEvent( + _$enumDecode(_$AutomationsEventTypeEnumMap, json['event_type'], + unknownValue: AutomationsEventType.unknown), + QMapper.dateTimeFromSecondsTimestamp(json['date'] as num), + ); +} + +Map _$AutomationsEventToJson(AutomationsEvent instance) => + { + 'event_type': _$AutomationsEventTypeEnumMap[instance.type], + 'date': instance.date.toIso8601String(), + }; + +K _$enumDecode( + Map enumValues, + Object? source, { + K? unknownValue, +}) { + if (source == null) { + throw ArgumentError( + 'A value must be provided. Supported values: ' + '${enumValues.values.join(', ')}', + ); + } + + return enumValues.entries.singleWhere( + (e) => e.value == source, + orElse: () { + if (unknownValue == null) { + throw ArgumentError( + '`$source` is not one of the supported values: ' + '${enumValues.values.join(', ')}', + ); + } + return MapEntry(unknownValue, enumValues.values.first); + }, + ).key; +} + +const _$AutomationsEventTypeEnumMap = { + AutomationsEventType.unknown: 0, + AutomationsEventType.trialStarted: 1, + AutomationsEventType.trialConverted: 2, + AutomationsEventType.trialCanceled: 3, + AutomationsEventType.trialBillingRetry: 4, + AutomationsEventType.subscriptionStarted: 5, + AutomationsEventType.subscriptionRenewed: 6, + AutomationsEventType.subscriptionRefunded: 7, + AutomationsEventType.subscriptionCanceled: 8, + AutomationsEventType.subscriptionBillingRetry: 9, + AutomationsEventType.inAppPurchase: 10, + AutomationsEventType.subscriptionUpgraded: 11, + AutomationsEventType.trialStillActive: 12, + AutomationsEventType.trialExpired: 13, + AutomationsEventType.subscriptionExpired: 14, + AutomationsEventType.subscriptionDowngraded: 15, + AutomationsEventType.subscriptionProductChanged: 16, +}; diff --git a/lib/src/models/automations/event_type.dart b/lib/src/models/automations/event_type.dart new file mode 100644 index 0000000..088a62c --- /dev/null +++ b/lib/src/models/automations/event_type.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; + +enum AutomationsEventType { + @JsonValue(0) + unknown, + @JsonValue(1) + trialStarted, + @JsonValue(2) + trialConverted, + @JsonValue(3) + trialCanceled, + @JsonValue(4) + trialBillingRetry, + @JsonValue(5) + subscriptionStarted, + @JsonValue(6) + subscriptionRenewed, + @JsonValue(7) + subscriptionRefunded, + @JsonValue(8) + subscriptionCanceled, + @JsonValue(9) + subscriptionBillingRetry, + @JsonValue(10) + inAppPurchase, + @JsonValue(11) + subscriptionUpgraded, + @JsonValue(12) + trialStillActive, + @JsonValue(13) + trialExpired, + @JsonValue(14) + subscriptionExpired, + @JsonValue(15) + subscriptionDowngraded, + @JsonValue(16) + subscriptionProductChanged, +} diff --git a/lib/src/models/launch_result.dart b/lib/src/models/launch_result.dart index 53d45ba..fca6ba6 100644 --- a/lib/src/models/launch_result.dart +++ b/lib/src/models/launch_result.dart @@ -15,7 +15,7 @@ class QLaunchResult { /// Original Server response time @JsonKey( name: 'timestamp', - fromJson: QMapper.dateTimeFromSecondsTimestamp, + fromJson: QMapper.dateTimeFromNullableSecondsTimestamp, ) final DateTime? date; diff --git a/lib/src/models/launch_result.g.dart b/lib/src/models/launch_result.g.dart index 79cd45a..f11eaa1 100644 --- a/lib/src/models/launch_result.g.dart +++ b/lib/src/models/launch_result.g.dart @@ -9,7 +9,7 @@ part of 'launch_result.dart'; QLaunchResult _$QLaunchResultFromJson(Map json) { return QLaunchResult( json['uid'] as String, - QMapper.dateTimeFromSecondsTimestamp(json['timestamp'] as num?), + QMapper.dateTimeFromNullableSecondsTimestamp(json['timestamp'] as num?), QMapper.productsFromJson(json['products']), QMapper.permissionsFromJson(json['permissions']), QMapper.productsFromJson(json['user_products']), diff --git a/lib/src/models/permission.dart b/lib/src/models/permission.dart index c8f3cad..12ad4bf 100644 --- a/lib/src/models/permission.dart +++ b/lib/src/models/permission.dart @@ -28,14 +28,14 @@ class QPermission { /// Purchase date @JsonKey( name: 'started_timestamp', - fromJson: QMapper.dateTimeFromSecondsTimestamp, + fromJson: QMapper.dateTimeFromNullableSecondsTimestamp, ) final DateTime? startedDate; /// Expiration date for subscription @JsonKey( name: 'expiration_timestamp', - fromJson: QMapper.dateTimeFromSecondsTimestamp, + fromJson: QMapper.dateTimeFromNullableSecondsTimestamp, ) final DateTime? expirationDate; diff --git a/lib/src/models/permission.g.dart b/lib/src/models/permission.g.dart index 67245d7..0d2a784 100644 --- a/lib/src/models/permission.g.dart +++ b/lib/src/models/permission.g.dart @@ -12,8 +12,10 @@ QPermission _$QPermissionFromJson(Map json) { json['associated_product'] as String, _$enumDecode(_$QProductRenewStateEnumMap, json['renew_state'], unknownValue: QProductRenewState.unknown), - QMapper.dateTimeFromSecondsTimestamp(json['started_timestamp'] as num?), - QMapper.dateTimeFromSecondsTimestamp(json['expiration_timestamp'] as num?), + QMapper.dateTimeFromNullableSecondsTimestamp( + json['started_timestamp'] as num?), + QMapper.dateTimeFromNullableSecondsTimestamp( + json['expiration_timestamp'] as num?), json['active'] as bool, ); } diff --git a/lib/src/models/product.g.dart b/lib/src/models/product.g.dart index 32e5b01..b94f7be 100644 --- a/lib/src/models/product.g.dart +++ b/lib/src/models/product.g.dart @@ -25,9 +25,9 @@ QProduct _$QProductFromJson(Map json) { Map _$QProductToJson(QProduct instance) => { 'id': instance.qonversionId, 'store_id': instance.storeId, + 'pretty_price': instance.prettyPrice, 'type': _$QProductTypeEnumMap[instance.type], 'duration': _$QProductDurationEnumMap[instance.duration], - 'pretty_price': instance.prettyPrice, 'trial_duration': _$QTrialDurationEnumMap[instance.trialDuration], 'sk_product': instance.skProduct, 'sku_details': instance.skuDetails, diff --git a/lib/src/models/qonversion_error.dart b/lib/src/models/qonversion_error.dart new file mode 100644 index 0000000..42072da --- /dev/null +++ b/lib/src/models/qonversion_error.dart @@ -0,0 +1,30 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'qonversion_error.g.dart'; + +@JsonSerializable() +class QError { + /// Qonversion Error Code + /// See more in [documentation](https://documentation.qonversion.io/docs/handling-errors) + @JsonKey(name: 'code') + final String code; + + /// Error description + @JsonKey(name: 'description') + final String message; + + /// Error details + @JsonKey(name: 'additionalMessage') + final String? details; + + const QError(this.code, this.message, this.details); + + factory QError.fromJson(Map json) => _$QErrorFromJson(json); + + Map toJson() => _$QErrorToJson(this); + + @override + String toString() { + return 'Qonversion Error.\nCode: $code, Message: $message, Details: $details'; + } +} diff --git a/lib/src/models/qonversion_error.g.dart b/lib/src/models/qonversion_error.g.dart new file mode 100644 index 0000000..9ed005f --- /dev/null +++ b/lib/src/models/qonversion_error.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'qonversion_error.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +QError _$QErrorFromJson(Map json) { + return QError( + json['code'] as String, + json['description'] as String, + json['additionalMessage'] as String?, + ); +} + +Map _$QErrorToJson(QError instance) => { + 'code': instance.code, + 'description': instance.message, + 'additionalMessage': instance.details, + }; diff --git a/lib/src/models/utils/mapper.dart b/lib/src/models/utils/mapper.dart index edc9c25..4b4b4d3 100644 --- a/lib/src/models/utils/mapper.dart +++ b/lib/src/models/utils/mapper.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../qonversion_error.dart'; import '../eligibility.dart'; import '../offerings.dart'; import '../permission.dart'; @@ -100,10 +101,27 @@ class QMapper { } } - static DateTime? dateTimeFromSecondsTimestamp(num? timestamp) { + static DateTime? dateTimeFromNullableSecondsTimestamp(num? timestamp) { if (timestamp == null) return null; + return dateTimeFromSecondsTimestamp(timestamp); + } + + static DateTime dateTimeFromSecondsTimestamp(num timestamp) { final intAbsTimestamp = timestamp.toInt().abs(); return DateTime.fromMillisecondsSinceEpoch(intAbsTimestamp); } + + static QError? qonversionErrorFromJson(dynamic json) { + if (json == null) return null; + + final map = Map.from(json); + + try { + return QError.fromJson(map); + } catch (e) { + print('Could not parse QError: $e'); + return null; + } + } } diff --git a/lib/src/qonversion.dart b/lib/src/qonversion.dart index a26b0dd..62742bd 100644 --- a/lib/src/qonversion.dart +++ b/lib/src/qonversion.dart @@ -295,9 +295,36 @@ class Qonversion { } /// Enable attribution collection from Apple Search Ads. NO by default. - static Future setAppleSearchAdsAttributionEnabled(bool enable) => - _channel.invokeMethod(Constants.mSetAppleSearchAdsAttributionEnabled, + static Future setAppleSearchAdsAttributionEnabled(bool enable) async { + if (Platform.isIOS) { + return _channel.invokeMethod( + Constants.mSetAppleSearchAdsAttributionEnabled, {Constants.kEnableAppleSearchAdsAttribution: enable}); + } + } + + /// Set push token to Qonversion to enable Qonversion push notifications + /// [token] Firebase device token on Android. APNs device token on iOS + static Future setNotificationsToken(String token) { + return _channel.invokeMethod(Constants.mSetNotificationsToken, + {Constants.kNotificationsToken: token}); + } + + /// [notificationData] notification payload data + /// See [Firebase RemoteMessage data](https://pub.dev/documentation/firebase_messaging_platform_interface/latest/firebase_messaging_platform_interface/RemoteMessage/data.html) + /// See [APNs notification data](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/1649869-userinfo) + /// Returns true when a push notification was received from Qonversion. Otherwise returns false, so you need to handle a notification yourself + static Future handleNotification( + Map notificationData) async { + try { + final bool rawResult = await _channel.invokeMethod( + Constants.mHandleNotification, + {Constants.kNotificationData: notificationData}) as bool; + return rawResult; + } catch (e) { + return false; + } + } // Private methods static Future _storeSdkInfo() => diff --git a/pubspec.lock b/pubspec.lock index 2e23f9b..72495b7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,21 +14,21 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "1.7.1" + version: "1.7.2" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.3.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -42,7 +42,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" build_config: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" build_runner: dependency: "direct dev" description: @@ -84,28 +84,28 @@ packages: name: built_collection url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "5.1.1" built_value: dependency: transitive description: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.1.0" + version: "8.1.3" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -119,7 +119,7 @@ packages: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.3.2" + version: "0.3.5" clock: dependency: transitive description: @@ -161,7 +161,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.1.1" fake_async: dependency: transitive description: @@ -199,7 +199,7 @@ packages: name: glob url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" graphs: dependency: transitive description: @@ -227,7 +227,7 @@ packages: name: io url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" js: dependency: transitive description: @@ -241,49 +241,49 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.1.0" json_serializable: dependency: "direct dev" description: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "4.1.3" + version: "4.1.4" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" mime: dependency: transitive description: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" package_config: dependency: transitive description: name: package_config url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" path: dependency: transitive description: @@ -311,21 +311,21 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" pubspec_parse: dependency: transitive description: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.1.4" + version: "1.2.0" shelf_web_socket: dependency: transitive description: @@ -344,14 +344,14 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -393,7 +393,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.4.3" timing: dependency: transitive description: @@ -421,7 +421,7 @@ packages: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" web_socket_channel: dependency: transitive description: @@ -437,5 +437,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.10.0" From 65bfd9f9f6b1392b3a337828deade06ba6f2a005 Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Wed, 24 Nov 2021 18:07:56 +0300 Subject: [PATCH 4/8] Remove files added by mistake --- example/lib/constants.dart | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 example/lib/constants.dart diff --git a/example/lib/constants.dart b/example/lib/constants.dart deleted file mode 100644 index 9c1759d..0000000 --- a/example/lib/constants.dart +++ /dev/null @@ -1,5 +0,0 @@ -class Constants { - // Firebase - static const channelId = 'high_importance_channel'; - static const channelName = 'High Importance Notifications'; -} From edcb9cbb1c276a1817b2d3f78efb11d6324169fe Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Wed, 24 Nov 2021 23:57:54 +0300 Subject: [PATCH 5/8] Fix AutomationDelegate reference issue --- .../AutomationsPlugin.kt | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt index 801ca64..f746089 100644 --- a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt +++ b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt @@ -12,6 +12,7 @@ class AutomationsPlugin { private var failedActionsStreamHandler: BaseEventStreamHandler? = null private var finishedActionsStreamHandler: BaseEventStreamHandler? = null private var finishedAutomationsStreamHandler: BaseEventStreamHandler? = null + private val automationsDelegate = getAutomationsDelegate() companion object { const val EVENT_CHANNEL_SHOWN_SCREENS = "shown_screens" @@ -44,29 +45,31 @@ class AutomationsPlugin { } fun setAutomationsDelegate() { - Automations.setDelegate(object : AutomationsDelegate { - override fun automationsDidShowScreen(screenId: String) { - shownScreensStreamHandler?.eventSink?.success(screenId) - } + Automations.setDelegate(automationsDelegate) + } + + private fun getAutomationsDelegate() = object : AutomationsDelegate { + override fun automationsDidShowScreen(screenId: String) { + shownScreensStreamHandler?.eventSink?.success(screenId) + } - override fun automationsDidStartExecuting(actionResult: QActionResult) { - val payload = Gson().toJson(actionResult.toMap()) - startedActionsStreamHandler?.eventSink?.success(payload) - } + override fun automationsDidStartExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + startedActionsStreamHandler?.eventSink?.success(payload) + } - override fun automationsDidFailExecuting(actionResult: QActionResult) { - val payload = Gson().toJson(actionResult.toMap()) - failedActionsStreamHandler?.eventSink?.success(payload) - } + override fun automationsDidFailExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + failedActionsStreamHandler?.eventSink?.success(payload) + } - override fun automationsDidFinishExecuting(actionResult: QActionResult) { - val payload = Gson().toJson(actionResult.toMap()) - finishedActionsStreamHandler?.eventSink?.success(payload) - } + override fun automationsDidFinishExecuting(actionResult: QActionResult) { + val payload = Gson().toJson(actionResult.toMap()) + finishedActionsStreamHandler?.eventSink?.success(payload) + } - override fun automationsFinished() { - finishedAutomationsStreamHandler?.eventSink?.success(null) - } - }) + override fun automationsFinished() { + finishedAutomationsStreamHandler?.eventSink?.success(null) + } } } \ No newline at end of file From bd80dc9247b160b5730822b4c8b008d49c3d7e1e Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Thu, 25 Nov 2021 13:05:54 +0300 Subject: [PATCH 6/8] Make constants private for AutomationsPlugin.kt --- .../sdk/qonversion_flutter_sdk/AutomationsPlugin.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt index f746089..09ecba4 100644 --- a/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt +++ b/android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/AutomationsPlugin.kt @@ -15,11 +15,11 @@ class AutomationsPlugin { private val automationsDelegate = getAutomationsDelegate() companion object { - const val EVENT_CHANNEL_SHOWN_SCREENS = "shown_screens" - const val EVENT_CHANNEL_STARTED_ACTIONS = "started_actions" - const val EVENT_CHANNEL_FAILED_ACTIONS = "failed_actions" - const val EVENT_CHANNEL_FINISHED_ACTIONS = "finished_actions" - const val EVENT_CHANNEL_FINISHED_AUTOMATIONS = "finished_automations" + private const val EVENT_CHANNEL_SHOWN_SCREENS = "shown_screens" + private const val EVENT_CHANNEL_STARTED_ACTIONS = "started_actions" + private const val EVENT_CHANNEL_FAILED_ACTIONS = "failed_actions" + private const val EVENT_CHANNEL_FINISHED_ACTIONS = "finished_actions" + private const val EVENT_CHANNEL_FINISHED_AUTOMATIONS = "finished_automations" } fun register(messenger: BinaryMessenger) { From a6493db8601c74a89930a220c15360fe03d44940 Mon Sep 17 00:00:00 2001 From: Maria-Bordunova Date: Thu, 25 Nov 2021 11:11:18 +0000 Subject: [PATCH 7/8] [create-pull-request] automated change --- CHANGELOG.md | 3 +++ lib/src/qonversion.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9b0dd..fb89f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 4.3.0 +* // Update changelog here + ## 4.2.0 * Add `setAppleSearchAdsAttributionEnabled()` method diff --git a/lib/src/qonversion.dart b/lib/src/qonversion.dart index 62742bd..6b85afe 100644 --- a/lib/src/qonversion.dart +++ b/lib/src/qonversion.dart @@ -16,7 +16,7 @@ import 'models/purchase_exception.dart'; import 'qa_provider.dart'; class Qonversion { - static const String _sdkVersion = "4.2.0"; + static const String _sdkVersion = "4.3.0"; static const MethodChannel _channel = MethodChannel('qonversion_flutter_sdk'); diff --git a/pubspec.yaml b/pubspec.yaml index d8ae7a9..34ac695 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: qonversion_flutter description: Flutter plugin to implement in-app subscriptions and purchases. Validate user receipts and manage cross-platform access to paid content on your app. Android & iOS. -version: 4.2.0 +version: 4.3.0 homepage: 'https://qonversion.io' repository: 'https://github.com/qonversion/flutter-sdk' From 0a78d8518ab9856930bfcaf92d90c8c725293777 Mon Sep 17 00:00:00 2001 From: Maria Bordunova Date: Thu, 25 Nov 2021 14:31:06 +0300 Subject: [PATCH 8/8] Bump version to 4.2.0 --- CHANGELOG.md | 4 +++- android/build.gradle | 2 +- ios/qonversion_flutter.podspec | 2 +- macos/qonversion_flutter.podspec | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb89f67..e6c5dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 4.3.0 -* // Update changelog here +* Qonversion Automation allows sending automated, personalized push notifications and in-app messages initiated by in-app purchase events. +This feature is designed to increase your app's revenue and retention, provide cancellation insights, reduce subscriber churn, and improve your subscribers' user experience. +See more in the [documentation](https://documentation.qonversion.io/docs/automations). ## 4.2.0 * Add `setAppleSearchAdsAttributionEnabled()` method diff --git a/android/build.gradle b/android/build.gradle index ceb6b88..e180ee9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.qonversion.flutter.sdk.qonversion_flutter_sdk' -version '4.1.1' +version '4.2.0' buildscript { ext.kotlin_version = '1.3.50' diff --git a/ios/qonversion_flutter.podspec b/ios/qonversion_flutter.podspec index 7a93a3a..fe3d667 100644 --- a/ios/qonversion_flutter.podspec +++ b/ios/qonversion_flutter.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'qonversion_flutter' - s.version = '3.4.0' + s.version = '3.5.0' s.summary = 'Flutter Qonversion SDK' s.description = <<-DESC Powerful yet simple subscription analytics diff --git a/macos/qonversion_flutter.podspec b/macos/qonversion_flutter.podspec index 1e69312..925113c 100644 --- a/macos/qonversion_flutter.podspec +++ b/macos/qonversion_flutter.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'qonversion_flutter' - s.version = '2.4.0' + s.version = '2.5.0' s.summary = 'Flutter Qonversion SDK' s.description = <<-DESC Powerful yet simple subscription analytics