Skip to content

Commit fdcf113

Browse files
committed
Merge branch 'release/1.0.0'
2 parents 2adbe38 + 54dbb17 commit fdcf113

17 files changed

+207
-270
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.0.0
2+
3+
* API update: new `launch` method with just one API key argument for both platforms. Contact us at [email protected] to merge your old API keys into one, [we can do it now](https://qonversion.io/docs/crossplatform-project).
4+
* API update: Remove old `launch` method; remove all old `launch...` methods.
5+
* Add `trackPurchase` method to track Android purchases manually.
6+
* Add `addAttributionData` method implementation for Android.
7+
18
## 0.3.0
29

310
* API update: new `launch` method, deprecate all old `launch...` methods

README.md

+25-8
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ To use Qonversion in your Flutter app, add `qonversion` as a [dependency in your
1616

1717
```
1818
dependencies:
19-
qonversion_flutter: ^0.3.0
19+
qonversion_flutter: ^1.0.0
2020
```
2121

2222
Run `flutter pub get` to install dependency.
2323

2424
## Usage
25+
26+
### Setup
27+
2528
You need to configure Qonversion once at a starting point of your app.
2629

2730
For example, launch Qonversion in `initState` of your top level widget:
@@ -42,28 +45,42 @@ void initState() {
4245
}
4346
4447
Future<String> _launchQonversion() async {
45-
_qonversionUserId = await Qonversion.launch(
46-
iosApiKey: 'YOUR_IOS_API_KEY',
47-
androidApiKey: 'YOUR_ANDROID_API_KEY',
48-
);
48+
_qonversionUserId = await Qonversion.launch('YOUR_API_KEY');
4949
}
5050
5151
...
5252
```
5353

54-
Qonversion will track purchases automatically.
54+
**IMPORTANT**
55+
56+
On Android you must call `Qonversion.trackPurchase(skuDetails, purchase)` method on each purchase success in order to track purchases.
57+
58+
On iOS Qonversion will track purchases automatically.
5559

5660
You can also specify your client side `userId` (instead of Qonversion user-id) that will be used for matching data in the third party data:
5761

5862
```
5963
final userId = 'CLIENT_SIDE_USER_ID';
6064
Qonversion.launch(
61-
iosApiKey: 'YOUR_IOS_API_KEY',
62-
androidApiKey: 'YOUR_ANDROID_API_KEY',
65+
'YOUR_API_KEY',
6366
userId: userId,
6467
);
6568
```
6669

70+
### Attribution
71+
You need to have AppsFlyer SDK integrated in your app before starting with this integration. If you do not have Appsflyer integration yet, please use [this docs](https://pub.dev/packages/appsflyer_sdk#-readme-tab-).
72+
73+
On iOS you can also use Branch integration.
74+
75+
Use `addAttributionData(data, provider, userId)` method to pass attribution data dictionary:
76+
```
77+
Qonversion.addAttributionData(
78+
data,
79+
QAttributionProvider.appsFlyer,
80+
'USER_ID',
81+
)
82+
```
83+
6784
## License
6885

6986
Qonversion SDK is available under the MIT license.

android/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@ android {
4141

4242
dependencies {
4343
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
44-
implementation "com.qonversion.android.sdk:sdk:0.3.0"
44+
implementation "com.qonversion.android.sdk:sdk:1.0.5"
4545
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
#Sun Jul 05 12:59:07 MSK 2020
12
distributionBase=GRADLE_USER_HOME
23
distributionPath=wrapper/dists
34
zipStoreBase=GRADLE_USER_HOME
45
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/FlutterResult+CustomErrors.kt

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ fun MethodChannel.Result.noAutoTrackPurchasesError() {
1818
return this.error("3", "Could not find autoTrackPurchases boolean value", "Make sure you pass Map as call arguments")
1919
}
2020

21+
fun MethodChannel.Result.noDataError() {
22+
return this.error("4", "Could not find data", "Please make sure you pass a valid value")
23+
}
24+
25+
fun MethodChannel.Result.noProviderError() {
26+
return this.error("5", "Could not find provider", "Please make sure you pass a valid value")
27+
}
28+
2129
fun MethodChannel.Result.qonversionError(message: String, cause: String) {
2230
return this.error("9", message, cause)
2331
}

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

+56-49
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ package com.qonversion.flutter.sdk.qonversion_flutter_sdk
22

33
import android.app.Activity
44
import android.app.Application
5-
import androidx.annotation.NonNull;
5+
import androidx.annotation.NonNull
6+
import com.android.billingclient.api.Purchase
7+
import com.android.billingclient.api.SkuDetails
8+
import com.qonversion.android.sdk.AttributionSource
69
import io.flutter.plugin.common.MethodCall
710
import io.flutter.plugin.common.MethodChannel
811
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
912
import io.flutter.plugin.common.MethodChannel.Result
1013
import io.flutter.plugin.common.PluginRegistry.Registrar
1114

12-
import com.qonversion.android.sdk.Qonversion;
13-
import com.qonversion.android.sdk.QonversionBillingBuilder
15+
import com.qonversion.android.sdk.Qonversion
1416
import com.qonversion.android.sdk.QonversionCallback
1517

1618
/** QonversionFlutterSdkPlugin */
@@ -27,40 +29,24 @@ class QonversionFlutterSdkPlugin internal constructor(registrar: Registrar): Met
2729
}
2830

2931
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
30-
val args = call.arguments<Map<String, Any>>()
32+
val args = call.arguments() as? Map<String, Any> ?: return result.noArgsError()
3133

32-
if (args == null || args.isEmpty()) {
33-
result.noArgsError()
34-
return
34+
if (args.isEmpty()) {
35+
return result.noArgsError()
3536
}
3637

37-
val apiKey = call.argument<String>("key")
38-
if (apiKey == null) {
39-
result.noApiKeyError()
40-
return
41-
}
42-
43-
val internalUserId = args["userID"] as? String ?: ""
44-
val autoTrackPurchases = args["autoTrackPurchases"] as? Boolean ?: true
45-
4638
when (call.method) {
47-
"launch" -> launch(apiKey, args, result)
48-
49-
// TODO remove when old methods get removed on Dart side
50-
"launchWithKeyCompletion",
51-
"launchWithKeyUserId",
52-
"launchWithKeyAutoTrackPurchasesCompletion" -> launchWith(apiKey, internalUserId, autoTrackPurchases, result)
53-
"addAttributionData" -> result.notImplemented() // since there is no such method in Android SDK
39+
"launch" -> launch(args, result)
40+
"trackPurchase" -> trackPurchase(args, result)
41+
"addAttributionData" -> addAttributionData(args, result)
5442
else -> result.notImplemented()
5543
}
5644
}
5745

58-
private fun launch(apiKey: String, args: Map<String, Any>, result: Result) {
59-
val userId = args["userID"] as? String ?: ""
46+
private fun launch(args: Map<String, Any>, result: Result) {
47+
val apiKey = args["key"] as? String ?: return result.noApiKeyError()
6048

61-
val billingBuilder = QonversionBillingBuilder()
62-
.enablePendingPurchases()
63-
.setListener { _, _ -> }
49+
val userId = args["userID"] as? String ?: ""
6450

6551
val callback = object: QonversionCallback {
6652
override fun onSuccess(uid: String) {
@@ -76,24 +62,18 @@ class QonversionFlutterSdkPlugin internal constructor(registrar: Registrar): Met
7662
application,
7763
apiKey,
7864
userId,
79-
billingBuilder,
80-
true,
8165
callback
8266
)
8367
}
8468

85-
// TODO remove when old methods get removed on Dart side
86-
private fun launchWith(key: String?,
87-
internalUserId: String = "",
88-
autoTrackPurchases: Boolean = true,
89-
result: Result) {
90-
if (key == null) {
91-
return result.error("1", "Could not find API key", "Please provide valid API key")
92-
}
69+
private fun trackPurchase(args: Map<String, Any>, result: Result) {
70+
@Suppress("UNCHECKED_CAST")
71+
val detailsMap = args["details"] as Map<String, Any>
72+
@Suppress("UNCHECKED_CAST")
73+
val purchaseMap = args["purchase"] as Map<String, Any>
9374

94-
val billingBuilder = QonversionBillingBuilder()
95-
.enablePendingPurchases()
96-
.setListener { _, _ -> }
75+
val details = createSkuDetails(detailsMap)
76+
val purchase = createPurchase(purchaseMap)
9777

9878
val callback = object: QonversionCallback {
9979
override fun onSuccess(uid: String) {
@@ -105,13 +85,40 @@ class QonversionFlutterSdkPlugin internal constructor(registrar: Registrar): Met
10585
}
10686
}
10787

108-
Qonversion.initialize(
109-
application,
110-
key,
111-
internalUserId,
112-
billingBuilder,
113-
autoTrackPurchases,
114-
callback
115-
)
88+
Qonversion.instance?.purchase(details, purchase, callback)
89+
}
90+
91+
private fun addAttributionData(args: Map<String, Any>, result: Result) {
92+
@Suppress("UNCHECKED_CAST")
93+
val data = args["data"] as? Map<String, Any> ?: return result.noDataError()
94+
95+
if (data.isEmpty()) {
96+
return result.noDataError()
97+
}
98+
99+
val provider = args["provider"] as? String ?: return result.noProviderError()
100+
101+
val uid = args["userId"] as? String ?: return result.noUserIdError()
102+
103+
val castedProvider = when (provider) {
104+
"appsFlyer" -> AttributionSource.APPSFLYER
105+
else -> null
106+
}
107+
?: return result.success(null)
108+
109+
Qonversion.instance?.attribution(data, castedProvider, uid)
110+
111+
result.success(null)
112+
}
113+
114+
private fun createSkuDetails(map: Map<String, Any>): SkuDetails {
115+
val json = map.toString()
116+
return SkuDetails(json)
117+
}
118+
119+
private fun createPurchase(map: Map<String, Any>): Purchase {
120+
val json = map.toString()
121+
val signature = map["signature"] as String
122+
return Purchase(json, signature)
116123
}
117124
}

example/android/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
}
77

88
dependencies {
9-
classpath 'com.android.tools.build:gradle:3.5.3'
9+
classpath 'com.android.tools.build:gradle:3.6.1'
1010
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1111
}
1212
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#Fri Jun 23 08:50:38 CEST 2017
1+
#Sun Jul 05 12:39:57 MSK 2020
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

example/lib/main.dart

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'dart:async';
33

4-
import 'package:qonversion_flutter/qonversion.dart';
4+
import 'package:qonversion_flutter/qonversion_flutter.dart';
55

66
void main() => runApp(MyApp());
77

@@ -22,10 +22,7 @@ class _MyAppState extends State<MyApp> {
2222
Future<void> initPlatformState() async {
2323
String uid;
2424
try {
25-
uid = await Qonversion.launch(
26-
iosApiKey: '',
27-
androidApiKey: '',
28-
);
25+
uid = await Qonversion.launch('');
2926
print(uid);
3027
} catch (e) {
3128
print('Failed to obtain uid from Qonversion.');

ios/Classes/SwiftQonversionFlutterSdkPlugin.swift

+6-38
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,6 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin {
2424
case "launch":
2525
launch(with: apiKey, args, result)
2626

27-
case "launchWithKeyCompletion":
28-
launch(with: apiKey, result)
29-
30-
case "launchWithKeyUserId":
31-
guard let userID = args["userID"] as? String else {
32-
result(FlutterError.noUserId)
33-
return
34-
}
35-
36-
launch(with: apiKey, userID: userID, result)
37-
38-
case "launchWithKeyAutoTrackPurchasesCompletion":
39-
guard let autoTrackPurchases = args["autoTrackPurchases"] as? Bool else {
40-
result(FlutterError.noAutoTrackPurchases)
41-
return
42-
}
43-
44-
launch(with: apiKey, autoTrackPurchases: autoTrackPurchases, result)
45-
4627
case "addAttributionData":
4728
addAttributionData(args: args, result)
4829

@@ -66,24 +47,6 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin {
6647
}
6748
}
6849

69-
private func launch(with key: String, _ result: @escaping FlutterResult) {
70-
Qonversion.launch(withKey: key) { uid in
71-
result(uid)
72-
}
73-
}
74-
75-
private func launch(with key: String, userID: String, _ result: @escaping FlutterResult) {
76-
Qonversion.launch(withKey: key, userID: userID)
77-
78-
result(nil)
79-
}
80-
81-
private func launch(with key: String, autoTrackPurchases: Bool, _ result: @escaping FlutterResult) {
82-
Qonversion.launch(withKey: key, autoTrackPurchases: autoTrackPurchases) { uid in
83-
result(uid)
84-
}
85-
}
86-
8750
private func addAttributionData(args: [String: Any], _ result: @escaping FlutterResult) {
8851
guard let data = args["data"] as? [AnyHashable: Any] else {
8952
result(FlutterError.noData)
@@ -95,6 +58,11 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin {
9558
return
9659
}
9760

61+
guard let userId = args["userId"] as? String else {
62+
result(FlutterError.noUserId)
63+
return
64+
}
65+
9866
// Using appsFlyer by default since there are only 2 cases in an enum yet.
9967
var castedProvider = QAttributionProvider.appsFlyer
10068

@@ -105,7 +73,7 @@ public class SwiftQonversionFlutterSdkPlugin: NSObject, FlutterPlugin {
10573
break
10674
}
10775

108-
Qonversion.addAttributionData(data, from: castedProvider)
76+
Qonversion.addAttributionData(data, from: castedProvider, userID: userId)
10977

11078
result(nil)
11179
}

ios/qonversion_flutter.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
Pod::Spec.new do |s|
66
s.name = 'qonversion_flutter'
7-
s.version = '0.3.0'
7+
s.version = '1.0.0'
88
s.summary = 'Flutter Qonversion SDK'
99
s.description = <<-DESC
1010
Powerful yet simple subscription analytics

0 commit comments

Comments
 (0)