Skip to content

Commit 398ef80

Browse files
authored
Merge pull request #425 from qonversion/release/11.1.0
2 parents be9f5fe + 10a0319 commit 398ef80

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4274
-819
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 11.1.0
2+
* No-Codes can now be used with your [custom purchase system](https://documentation.qonversion.io/docs/using-no-codes-with-custom-purchases-handling), even if you use Qonversion in Analytics mode,
3+
* You can now provide a custom locale to No-Codes to override the system one,
4+
* The new purchase method with comprehensive results added - with the status, entitlements, and store transaction info. Consider upgrading your purchases to the new method,
5+
* The sample application was fully reworked and became more comprehensive and useful.
6+
17
## 11.0.0
28
* No-Codes are now stable! This release brings lots of crucial features:
39
* [No-Code screens preloading](https://documentation.qonversion.io/update/docs/screens-preloading#/) support,

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ group 'com.qonversion.flutter.sdk.qonversion_flutter_sdk'
22
version '5.0.0'
33

44
buildscript {
5-
ext.kotlin_version = '1.6.10'
5+
ext.kotlin_version = '1.9.25'
66
repositories {
77
google()
88
mavenCentral()
@@ -51,6 +51,6 @@ android {
5151

5252
dependencies {
5353
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
54-
implementation "io.qonversion:sandwich:7.1.0"
54+
implementation "io.qonversion:sandwich:7.3.0"
5555
implementation 'com.google.code.gson:gson:2.9.0'
5656
}

android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/NoCodesPlugin.kt

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import io.flutter.plugin.common.BinaryMessenger
55
import io.flutter.plugin.common.MethodChannel.Result
66
import io.qonversion.sandwich.BridgeData
77
import io.qonversion.sandwich.NoCodesEventListener
8+
import io.qonversion.sandwich.NoCodesPurchaseDelegateBridge
89
import io.qonversion.sandwich.NoCodesSandwich
910
import com.google.gson.Gson
1011

11-
class NoCodesPlugin(private val messenger: BinaryMessenger, private val context: Context) : NoCodesEventListener {
12+
class NoCodesPlugin(private val messenger: BinaryMessenger, private val context: Context) : NoCodesEventListener, NoCodesPurchaseDelegateBridge {
1213
private var noCodesSandwich: NoCodesSandwich? = null
1314
private val gson = Gson()
1415

@@ -19,6 +20,10 @@ class NoCodesPlugin(private val messenger: BinaryMessenger, private val context:
1920
private var actionFailedEventStreamHandler: BaseEventStreamHandler? = null
2021
private var actionFinishedEventStreamHandler: BaseEventStreamHandler? = null
2122
private var screenFailedToLoadEventStreamHandler: BaseEventStreamHandler? = null
23+
24+
// Purchase delegate event stream handlers
25+
private var purchaseEventStreamHandler: BaseEventStreamHandler? = null
26+
private var restoreEventStreamHandler: BaseEventStreamHandler? = null
2227

2328
companion object {
2429
private const val SCREEN_SHOWN_EVENT_CHANNEL = "nocodes_screen_shown"
@@ -27,6 +32,8 @@ class NoCodesPlugin(private val messenger: BinaryMessenger, private val context:
2732
private const val ACTION_FAILED_EVENT_CHANNEL = "nocodes_action_failed"
2833
private const val ACTION_FINISHED_EVENT_CHANNEL = "nocodes_action_finished"
2934
private const val SCREEN_FAILED_TO_LOAD_EVENT_CHANNEL = "nocodes_screen_failed_to_load"
35+
private const val PURCHASE_EVENT_CHANNEL = "nocodes_purchase"
36+
private const val RESTORE_EVENT_CHANNEL = "nocodes_restore"
3037
}
3138

3239
init {
@@ -58,20 +65,30 @@ class NoCodesPlugin(private val messenger: BinaryMessenger, private val context:
5865
val screenFailedToLoadListener = BaseListenerWrapper(messenger, SCREEN_FAILED_TO_LOAD_EVENT_CHANNEL)
5966
screenFailedToLoadListener.register()
6067
this.screenFailedToLoadEventStreamHandler = screenFailedToLoadListener.eventStreamHandler
68+
69+
// Register purchase delegate event channels
70+
val purchaseListener = BaseListenerWrapper(messenger, PURCHASE_EVENT_CHANNEL)
71+
purchaseListener.register()
72+
this.purchaseEventStreamHandler = purchaseListener.eventStreamHandler
73+
74+
val restoreListener = BaseListenerWrapper(messenger, RESTORE_EVENT_CHANNEL)
75+
restoreListener.register()
76+
this.restoreEventStreamHandler = restoreListener.eventStreamHandler
6177
}
6278

6379
fun initializeNoCodes(args: Map<String, Any>, result: Result) {
6480
val projectKey = args["projectKey"] as? String ?: return result.noNecessaryDataError()
6581
val version = args["version"] as? String ?: return result.noNecessaryDataError()
6682
val source = args["source"] as? String ?: return result.noNecessaryDataError()
83+
val locale = args["locale"] as? String
6784

6885
if (projectKey.isNotEmpty()) {
6986
// Initialize NoCodes Sandwich
7087
noCodesSandwich = NoCodesSandwich()
7188

7289
noCodesSandwich?.storeSdkInfo(context, source, version)
7390

74-
noCodesSandwich?.initialize(context, projectKey)
91+
noCodesSandwich?.initialize(context, projectKey, null, null, null, locale)
7592
noCodesSandwich?.setDelegate(this)
7693
result.success(null)
7794
} else {
@@ -102,6 +119,38 @@ class NoCodesPlugin(private val messenger: BinaryMessenger, private val context:
102119
result.success(null)
103120
}
104121

122+
fun setLocale(locale: String?, result: Result) {
123+
noCodesSandwich?.setLocale(locale)
124+
result.success(null)
125+
}
126+
127+
// MARK: - Purchase Delegate Methods
128+
129+
fun setPurchaseDelegate(result: Result) {
130+
noCodesSandwich?.setPurchaseDelegate(this)
131+
result.success(null)
132+
}
133+
134+
fun delegatedPurchaseCompleted(result: Result) {
135+
noCodesSandwich?.delegatedPurchaseCompleted()
136+
result.success(null)
137+
}
138+
139+
fun delegatedPurchaseFailed(errorMessage: String?, result: Result) {
140+
noCodesSandwich?.delegatedPurchaseFailed(errorMessage ?: "Unknown error")
141+
result.success(null)
142+
}
143+
144+
fun delegatedRestoreCompleted(result: Result) {
145+
noCodesSandwich?.delegatedRestoreCompleted()
146+
result.success(null)
147+
}
148+
149+
fun delegatedRestoreFailed(errorMessage: String?, result: Result) {
150+
noCodesSandwich?.delegatedRestoreFailed(errorMessage ?: "Unknown error")
151+
result.success(null)
152+
}
153+
105154
// NoCodesEventListener implementation
106155
override fun onNoCodesEvent(event: NoCodesEventListener.Event, payload: BridgeData?) {
107156
val eventData = mapOf("payload" to (payload ?: emptyMap<String, Any>()))
@@ -130,4 +179,14 @@ class NoCodesPlugin(private val messenger: BinaryMessenger, private val context:
130179
}
131180
}
132181
}
133-
}
182+
183+
// NoCodesPurchaseDelegateBridge implementation
184+
override fun purchase(product: BridgeData) {
185+
val jsonString = gson.toJson(product)
186+
purchaseEventStreamHandler?.eventSink?.success(jsonString)
187+
}
188+
189+
override fun restore() {
190+
restoreEventStreamHandler?.eventSink?.success("restore")
191+
}
192+
}

android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionPlugin.kt

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,31 @@ class QonversionPlugin : MethodCallHandler, FlutterPlugin, ActivityAware {
111111
"remoteConfigList" -> {
112112
return remoteConfigList(result)
113113
}
114-
"closeNoCodes" -> noCodesPlugin?.closeNoCodes(result)
114+
"closeNoCodes" -> {
115+
noCodesPlugin?.closeNoCodes(result)
116+
return
117+
}
118+
// NoCodes Purchase Delegate methods without args
119+
"setNoCodesPurchaseDelegate" -> {
120+
noCodesPlugin?.setPurchaseDelegate(result)
121+
return
122+
}
123+
"delegatedPurchaseCompleted" -> {
124+
noCodesPlugin?.delegatedPurchaseCompleted(result)
125+
return
126+
}
127+
"delegatedRestoreCompleted" -> {
128+
noCodesPlugin?.delegatedRestoreCompleted(result)
129+
return
130+
}
115131
}
116132

117133
// Methods with args
118134
val args = call.arguments() as? Map<String, Any> ?: return result.noNecessaryDataError()
119135
when (call.method) {
120136
"initialize" -> initialize(args, result)
121137
"purchase" -> purchase(args, result)
138+
"purchaseWithResult" -> purchaseWithResult(args, result)
122139
"updatePurchase" -> updatePurchase(args, result)
123140
"remoteConfig" -> remoteConfig(args["contextKey"] as? String, result)
124141
"remoteConfigListForContextKeys" -> remoteConfigList(args, result)
@@ -136,6 +153,10 @@ class QonversionPlugin : MethodCallHandler, FlutterPlugin, ActivityAware {
136153
"initializeNoCodes" -> noCodesPlugin?.initializeNoCodes(args, result)
137154
"setScreenPresentationConfig" -> noCodesPlugin?.setScreenPresentationConfig(args["config"] as? Map<String, Any>, args["contextKey"] as? String, result)
138155
"showNoCodesScreen" -> noCodesPlugin?.showNoCodesScreen(args["contextKey"] as? String, result)
156+
"setNoCodesLocale" -> noCodesPlugin?.setLocale(args["locale"] as? String, result)
157+
// NoCodes Purchase Delegate methods
158+
"delegatedPurchaseFailed" -> noCodesPlugin?.delegatedPurchaseFailed(args["errorMessage"] as? String, result)
159+
"delegatedRestoreFailed" -> noCodesPlugin?.delegatedRestoreFailed(args["errorMessage"] as? String, result)
139160
else -> result.notImplemented()
140161
}
141162
}
@@ -182,6 +203,27 @@ class QonversionPlugin : MethodCallHandler, FlutterPlugin, ActivityAware {
182203
)
183204
}
184205

206+
private fun purchaseWithResult(args: Map<String, Any>, result: Result) {
207+
val productId = args["productId"] as? String ?: return result.noNecessaryDataError()
208+
val oldProductId = args["oldProductId"] as? String
209+
val offerId = args["offerId"] as? String
210+
val applyOffer = args["applyOffer"] as? Boolean
211+
val updatePolicyKey = args["updatePolicyKey"] as? String
212+
213+
@Suppress("UNCHECKED_CAST")
214+
val contextKeys = args["contextKeys"] as? List<String>
215+
216+
qonversionSandwich.purchaseWithResult(
217+
productId,
218+
offerId,
219+
applyOffer,
220+
oldProductId,
221+
updatePolicyKey,
222+
contextKeys,
223+
result.toJsonResultListener()
224+
)
225+
}
226+
185227
private fun updatePurchase(args: Map<String, Any>, result: Result) {
186228
purchase(args, result)
187229
}

example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ android {
3333
defaultConfig {
3434
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
3535
applicationId "io.qonversion.sampleapp"
36-
minSdkVersion 21
36+
minSdkVersion 23
3737
targetSdkVersion 34
3838
versionCode flutterVersionCode.toInteger()
3939
versionName flutterVersionName

example/ios/Flutter/ephemeral/flutter_lldb_helper.py

Lines changed: 0 additions & 32 deletions
This file was deleted.

example/ios/Flutter/ephemeral/flutter_lldbinit

Lines changed: 0 additions & 5 deletions
This file was deleted.

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1111
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
1212
4CB27CA1276762D900DE51FD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4CB27CA0276762D900DE51FD /* GoogleService-Info.plist */; };
13-
70A6004A2A4DE85F00B4378E /* QonversionPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 70A600432A4DE85F00B4378E /* QonversionPlugin.m */; };
14-
70A6004B2A4DE85F00B4378E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A600442A4DE85F00B4378E /* Extensions.swift */; };
15-
70A6004C2A4DE85F00B4378E /* SwiftQonversionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A600462A4DE85F00B4378E /* SwiftQonversionPlugin.swift */; };
16-
70A6004D2A4DE85F00B4378E /* BaseListenerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A600472A4DE85F00B4378E /* BaseListenerWrapper.swift */; };
17-
70A6004E2A4DE85F00B4378E /* BaseEventStreamHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A600482A4DE85F00B4378E /* BaseEventStreamHandler.swift */; };
18-
70A6004F2A4DE85F00B4378E /* FlutterError+Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A600492A4DE85F00B4378E /* FlutterError+Custom.swift */; };
1913
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
2014
8876821AD52624FFC742CB13 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B61BD248B3C1CEFD8465871F /* Pods_Runner.framework */; };
2115
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
@@ -43,13 +37,6 @@
4337
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4438
4CB27CA0276762D900DE51FD /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
4539
6A76C3422936312F0071A79A /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
46-
70A600432A4DE85F00B4378E /* QonversionPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QonversionPlugin.m; sourceTree = "<group>"; };
47-
70A600442A4DE85F00B4378E /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
48-
70A600452A4DE85F00B4378E /* QonversionPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QonversionPlugin.h; sourceTree = "<group>"; };
49-
70A600462A4DE85F00B4378E /* SwiftQonversionPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftQonversionPlugin.swift; sourceTree = "<group>"; };
50-
70A600472A4DE85F00B4378E /* BaseListenerWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseListenerWrapper.swift; sourceTree = "<group>"; };
51-
70A600482A4DE85F00B4378E /* BaseEventStreamHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseEventStreamHandler.swift; sourceTree = "<group>"; };
52-
70A600492A4DE85F00B4378E /* FlutterError+Custom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FlutterError+Custom.swift"; sourceTree = "<group>"; };
5340
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
5441
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5542
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@@ -85,21 +72,6 @@
8572
name = Frameworks;
8673
sourceTree = "<group>";
8774
};
88-
70A600422A4DE85F00B4378E /* Classes */ = {
89-
isa = PBXGroup;
90-
children = (
91-
70A600432A4DE85F00B4378E /* QonversionPlugin.m */,
92-
70A600442A4DE85F00B4378E /* Extensions.swift */,
93-
70A600452A4DE85F00B4378E /* QonversionPlugin.h */,
94-
70A600462A4DE85F00B4378E /* SwiftQonversionPlugin.swift */,
95-
70A600472A4DE85F00B4378E /* BaseListenerWrapper.swift */,
96-
70A600482A4DE85F00B4378E /* BaseEventStreamHandler.swift */,
97-
70A600492A4DE85F00B4378E /* FlutterError+Custom.swift */,
98-
);
99-
name = Classes;
100-
path = ../../macos/Classes;
101-
sourceTree = "<group>";
102-
};
10375
9740EEB11CF90186004384FC /* Flutter */ = {
10476
isa = PBXGroup;
10577
children = (
@@ -114,7 +86,6 @@
11486
97C146E51CF9000F007C117D = {
11587
isa = PBXGroup;
11688
children = (
117-
70A600422A4DE85F00B4378E /* Classes */,
11889
4CB27CA0276762D900DE51FD /* GoogleService-Info.plist */,
11990
9740EEB11CF90186004384FC /* Flutter */,
12091
97C146F01CF9000F007C117D /* Runner */,
@@ -325,14 +296,8 @@
325296
isa = PBXSourcesBuildPhase;
326297
buildActionMask = 2147483647;
327298
files = (
328-
70A6004D2A4DE85F00B4378E /* BaseListenerWrapper.swift in Sources */,
329299
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
330-
70A6004F2A4DE85F00B4378E /* FlutterError+Custom.swift in Sources */,
331-
70A6004A2A4DE85F00B4378E /* QonversionPlugin.m in Sources */,
332300
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
333-
70A6004B2A4DE85F00B4378E /* Extensions.swift in Sources */,
334-
70A6004C2A4DE85F00B4378E /* SwiftQonversionPlugin.swift in Sources */,
335-
70A6004E2A4DE85F00B4378E /* BaseEventStreamHandler.swift in Sources */,
336301
);
337302
runOnlyForDeploymentPostprocessing = 0;
338303
};

0 commit comments

Comments
 (0)