diff --git a/README.md b/README.md index 7d71b99..4a010d8 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ ### This plugin is built for -- Android AppsFlyer SDK **v6.12.2** -- iOS AppsFlyer SDK **v6.12.2** +- Android AppsFlyer SDK **v6.13.0** +- iOS AppsFlyer SDK **v6.13.0** ## ❗❗ Breaking changes when updating to v6.x.x❗❗ diff --git a/android/build.gradle b/android/build.gradle index e66c54f..7475e8c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,6 +31,6 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'com.appsflyer:af-android-sdk:6.12.2' + implementation 'com.appsflyer:af-android-sdk:6.13.0' implementation 'com.android.installreferrer:installreferrer:2.1' } \ No newline at end of file diff --git a/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java b/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java index 0e5ca6e..6d4247d 100644 --- a/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java +++ b/android/src/main/java/com/appsflyer/appsflyersdk/AppsFlyerConstants.java @@ -1,11 +1,12 @@ package com.appsflyer.appsflyersdk; public class AppsFlyerConstants { - final static String PLUGIN_VERSION = "6.12.2"; + final static String PLUGIN_VERSION = "6.13.0"; final static String AF_APP_INVITE_ONE_LINK = "appInviteOneLink"; final static String AF_HOST_PREFIX = "hostPrefix"; final static String AF_HOST_NAME = "hostName"; final static String AF_IS_DEBUG = "isDebug"; + final static String AF_MANUAL_START = "manualStart"; final static String AF_DEV_KEY = "afDevKey"; final static String AF_EVENT_NAME = "eventName"; final static String AF_EVENT_VALUES = "eventValues"; diff --git a/android/src/main/java/com/appsflyer/appsflyersdk/AppsflyerSdkPlugin.java b/android/src/main/java/com/appsflyer/appsflyersdk/AppsflyerSdkPlugin.java index fbf59a7..20343f5 100644 --- a/android/src/main/java/com/appsflyer/appsflyersdk/AppsflyerSdkPlugin.java +++ b/android/src/main/java/com/appsflyer/appsflyersdk/AppsflyerSdkPlugin.java @@ -10,6 +10,7 @@ import android.util.Log; import com.appsflyer.AFLogger; +import com.appsflyer.AppsFlyerConsent; import com.appsflyer.AppsFlyerConversionListener; import com.appsflyer.AppsFlyerInAppPurchaseValidatorListener; import com.appsflyer.AppsFlyerLib; @@ -208,6 +209,9 @@ public void onMethodCall(MethodCall call, Result result) { case "initSdk": initSdk(call, result); break; + case "startSDK": + startSDK(call, result); + break; case "logEvent": logEvent(call, result); break; @@ -217,6 +221,12 @@ public void onMethodCall(MethodCall call, Result result) { case "setCurrencyCode": setCurrencyCode(call, result); break; + case "enableTCFDataCollection": + enableTCFDataCollection(call, result); + break; + case "setConsentData": + setConsentData(call, result); + break; case "setIsUpdate": setIsUpdate(call, result); break; @@ -330,7 +340,38 @@ public void onMethodCall(MethodCall call, Result result) { break; } } - + + private void startSDK(MethodCall call, Result result) { + AppsFlyerLib instance = AppsFlyerLib.getInstance(); + instance.start(activity); + } + + public void setConsentData(MethodCall call, Result result) { + Map arguments = (Map) call.arguments; + Map consentDict = (Map) arguments.get("consentData"); + + boolean isUserSubjectToGDPR = (boolean) consentDict.get("isUserSubjectToGDPR"); + Boolean hasConsentForDataUsage = (Boolean) consentDict.get("hasConsentForDataUsage"); + Boolean hasConsentForAdsPersonalization = (Boolean) consentDict.get("hasConsentForAdsPersonalization"); + + AppsFlyerConsent consentData; + if (isUserSubjectToGDPR && hasConsentForDataUsage != null && hasConsentForAdsPersonalization != null) { + consentData = AppsFlyerConsent.forGDPRUser(hasConsentForDataUsage, hasConsentForAdsPersonalization); + } else { + consentData = AppsFlyerConsent.forNonGDPRUser(); + } + + AppsFlyerLib.getInstance().setConsentData(consentData); + + + result.success(null); + } + private void enableTCFDataCollection(MethodCall call, Result result) { + boolean shouldCollect = (boolean) call.argument("shouldCollect"); + AppsFlyerLib.getInstance().enableTCFDataCollection(shouldCollect); + result.success(null); + } + private void addPushNotificationDeepLinkPath(MethodCall call, Result result) { if(call.arguments != null){ ArrayList depplinkPath = (ArrayList) call.arguments; @@ -776,6 +817,8 @@ private void initSdk(MethodCall call, final MethodChannel.Result result) { DeepLinkListener udlListener = null; AppsFlyerLib instance = AppsFlyerLib.getInstance(); + boolean isManualStartMode = (boolean) call.argument(AppsFlyerConstants.AF_MANUAL_START); + String afDevKey = (String) call.argument(AppsFlyerConstants.AF_DEV_KEY); if (afDevKey == null || afDevKey.equals("")) { result.error(null, "AF Dev Key is empty", "AF dev key cannot be empty"); @@ -814,7 +857,9 @@ private void initSdk(MethodCall call, final MethodChannel.Result result) { instance.setAppInviteOneLink(appInviteOneLink); } - instance.start(activity); + if (!isManualStartMode) { + instance.start(activity); + } if (saveCallbacks) { saveCallbacks = false; diff --git a/doc/API.md b/doc/API.md index 4663d19..82b6926 100644 --- a/doc/API.md +++ b/doc/API.md @@ -7,6 +7,7 @@ ## Methods - [initSdk](#initSdk) +- [startSDK](#startSDK) - [onAppOpenAttribution](#onAppOpenAttribution) - [onInstallConversionData](#onInstallConversionData) - [onDeepLinking](#onDeepLinking) @@ -37,6 +38,8 @@ - [addPushNotificationDeepLinkPath](#addPushNotificationDeepLinkPath) - [User Invite](#userInvite) - [enableFacebookDeferredApplinks](#enableFacebookDeferredApplinks) +- [enableTCFDataCollection](#enableTCFDataCollection) +- [setConsentData](#setConsentData) - [disableSKAdNetwork](#disableSKAdNetwork) - [getAppsFlyerUID](#getAppsFlyerUID) - [setCurrentDeviceLanguage](#setCurrentDeviceLanguage) @@ -64,11 +67,13 @@ | -------- | -------- | ------------- | | devKey | String | Your application's [devKey](https://support.appsflyer.com/hc/en-us/articles/207032066-Basic-SDK-integration-guide#retrieving-the-dev-key) provided by AppsFlyer (required) | | appId | String | Your application's [App ID](https://support.appsflyer.com/hc/en-us/articles/207377436-Adding-a-new-app#available-in-the-app-store-google-play-store-windows-phone-store) (required for iOS only) that you configured in your AppsFlyer dashboard | -| showDebug | bool | Debug mode - set to `true` for testing only, do not release to production with this parameter set to `true`! | -| timeToWaitForATTUserAuthorization | double | Delays the SDK start for x seconds until the user either accepts the consent dialog, declines it, or the timer runs out. | -| appInviteOneLink | String | The [OneLink template ID](https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution#parameters) that is used to generate a User Invite, this is not a required field in the `AppsFlyerOptions`, you may choose to set it later via the appropriate API. | +| showDebug | bool | Debug mode - set to `true` for testing only, do not release to production with this parameter set to `true`! | +| timeToWaitForATTUserAuthorization | double | Delays the SDK start for x seconds until the user either accepts the consent dialog, declines it, or the timer runs out. | +| appInviteOneLink | String | The [OneLink template ID](https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution#parameters) that is used to generate a User Invite, this is not a required field in the `AppsFlyerOptions`, you may choose to set it later via the appropriate API. | | disableAdvertisingIdentifier| bool | Opt-out of the collection of Advertising Identifiers, which include OAID, AAID, GAID and IDFA. | | disableCollectASA | bool | Opt-out of the Apple Search Ads attributions. | +| manualStart | bool | Prevents from the SDK from sending the launch request after using appsFlyer.initSdk(...). When using this property, the apps needs to manually trigger the appsFlyer.startSdk() API to report the app launch.| + @@ -128,6 +133,12 @@ _appsflyerSdk.initSdk( registerOnDeepLinkingCallback: true) ``` +--- +##### **`startSDK()` (Added in 6.13.0)** +In version 6.13.0 of the appslfyer-flutter-plugin SDK we added the option of splitting between the initialization stage and start stage. All you need to do is add the property manualStart: true to the init object, and later call appsFlyer.startSdk() whenever you decide. If this property is set to false or doesn’t exist, the sdk will start after calling appsFlyer.initSdk(...). +```dart +_appsflyerSdk.startSDK(); +``` --- #### **`onAppOpenAttribution(Func)` - Trigger callback when onAppOpenAttribution is activated on the native side @@ -258,6 +269,78 @@ _Example:_ ```dart appsFlyerSdk.enableLocationCollection(true); ``` +--- +** `enableTCFDataCollection(bool shouldCollect)`** + +The `enableTCFDataCollection` method is employed to control the automatic collection of the Transparency and Consent Framework (TCF) data. By setting this flag to `true`, the system is instructed to automatically collect TCF data. Conversely, setting it to `false` prevents such data collection. + +_Example:_ +```dart +appsFlyerSdk.enableTCFDataCollection(true); +``` +--- +** `setConsentData(Map consentData)`** + +The `AppsflyerConsent` object helps manage user consent settings. By using the setConsentData we able to manually collect the TCF data. You can create an instance for users subject to GDPR or otherwise: + +1. Users subjected to GDPR: + +```dart +var forGdpr = _appsflyerSdk.forGDPRUser( + hasConsentForDataUsage: true, + hasConsentForAdsPersonalization: true +); +_appsflyerSdk.setConsentData(forGdpr); +``` + +2. Users not subject to GDPR: + +```dart +var nonGdpr = _appsflyerSdk.nonGDPRUser(); +_appsflyerSdk.setConsentData(nonGdpr); +``` + +The `_appsflyerSdk` handles consent data with `setConsentData` method, where you can pass the desired `AppsflyerConsent` instance. + +--- +To reflect TCF data in the conversion (first launch) payload, it's crucial to configure `enableTCFDataCollection` **or** `setConsentData` between the SDK initialization and start phase. Follow the example provided: + +```dart +// Set AppsFlyerOption - make sure to set manualStart to true +final AppsFlyerOptions options = AppsFlyerOptions( + afDevKey: dotenv.env["DEV_KEY"]!, + appId: dotenv.env["APP_ID"]!, + showDebug: true, + timeToWaitForATTUserAuthorization: 15, + manualStart: true); +_appsflyerSdk = AppsflyerSdk(options); + +// Init the AppsFlyer SDK +_appsflyerSdk.initSdk( + registerConversionDataCallback: true, + registerOnAppOpenAttributionCallback: true, + registerOnDeepLinkingCallback: true); + +// Set configurations to the SDK +// Enable TCF Data Collection +_appsflyerSdk.enableTCFDataCollection(true); + +// Set Consent Data +// If user is subject to GDPR +// var forGdpr = _appsflyerSdk.forGDPRUser(hasConsentForDataUsage: true, hasConsentForAdsPersonalization: true); +// _appsflyerSdk.setConsentData(forGdpr); + +// If user is not subject to GDPR +var nonGdpr = _appsflyerSdk.nonGDPRUser(); +_appsflyerSdk.setConsentData(nonGdpr); + +// Here we start a session +_appsflyerSdk.startSDK(); +``` + +Following this sequence ensures that the consent configurations take effect before the AppsFlyer SDK starts, providing accurate consent data in the first launch payload. +Note: You need to use either `enableTCFDataCollection` or `setConsentData` if you use both of them our backend will prioritize the provided consent data from `setConsentData`. + --- ** `void setCustomerUserId(String userId)`** diff --git a/doc/BasicIntegration.md b/doc/BasicIntegration.md index 5ec81cb..2fddaad 100644 --- a/doc/BasicIntegration.md +++ b/doc/BasicIntegration.md @@ -13,20 +13,22 @@ AppsFlyerOptions appsFlyerOptions = AppsFlyerOptions( timeToWaitForATTUserAuthorization: 50, // for iOS 14.5 appInviteOneLink: oneLinkID, // Optional field disableAdvertisingIdentifier: false, // Optional field - disableCollectASA: false); // Optional field + disableCollectASA: false, //Optional field + manualStart: true, ); // Optional field AppsflyerSdk appsflyerSdk = AppsflyerSdk(appsFlyerOptions); ``` -| Setting | Type | Description | -| -------- | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| devKey | String | Your application's [devKey](https://support.appsflyer.com/hc/en-us/articles/207032066-Basic-SDK-integration-guide#retrieving-the-dev-key) provided by AppsFlyer (required) | -| appId | String | Your application's [App ID](https://support.appsflyer.com/hc/en-us/articles/207377436-Adding-a-new-app#available-in-the-app-store-google-play-store-windows-phone-store) (required for iOS only) that you configured in your AppsFlyer dashboard should be without the 'id' prefix | -| showDebug | bool | Debug mode - set to `true` for testing only, do not release to production with this parameter set to `true`! | -| timeToWaitForATTUserAuthorization | double | Delays the SDK start for x seconds until the user either accepts the consent dialog, declines it, or the timer runs out. | -| appInviteOneLink | String | The [OneLink template ID](https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution#parameters) that is used to generate a User Invite, this is not a required field in the `AppsFlyerOptions`, you may choose to set it later via the appropriate API. | -| disableAdvertisingIdentifier| bool | Opt-out of the collection of Advertising Identifiers, which include OAID, AAID, GAID and IDFA. | -| disableCollectASA | bool | Opt-out of the Apple Search Ads attributions. | +| Setting | Type | Description | +|-----------------------------------| -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| devKey | String | Your application's [devKey](https://support.appsflyer.com/hc/en-us/articles/207032066-Basic-SDK-integration-guide#retrieving-the-dev-key) provided by AppsFlyer (required) | +| appId | String | Your application's [App ID](https://support.appsflyer.com/hc/en-us/articles/207377436-Adding-a-new-app#available-in-the-app-store-google-play-store-windows-phone-store) (required for iOS only) that you configured in your AppsFlyer dashboard should be without the 'id' prefix | +| showDebug | bool | Debug mode - set to `true` for testing only, do not release to production with this parameter set to `true`! | +| timeToWaitForATTUserAuthorization | double | Delays the SDK start for x seconds until the user either accepts the consent dialog, declines it, or the timer runs out. | +| appInviteOneLink | String | The [OneLink template ID](https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution#parameters) that is used to generate a User Invite, this is not a required field in the `AppsFlyerOptions`, you may choose to set it later via the appropriate API. | +| disableAdvertisingIdentifier | bool | Opt-out of the collection of Advertising Identifiers, which include OAID, AAID, GAID and IDFA. | +| disableCollectASA | bool | Opt-out of the Apple Search Ads attributions. | +| manualStart | bool | Prevents from the SDK from sending the launch request after using appsFlyer.initSdk(...). When using this property, the apps needs to manually trigger the appsFlyer.startSdk() API to report the app launch. | The next step is to call `initSdk` which have the optional boolean parameters `registerConversionDataCallback` and the deeplink callbacks: `registerOnAppOpenAttributionCallback` `registerOnDeepLinkingCallback` @@ -46,4 +48,28 @@ appsflyerSdk.initSdk( | -------- | ------------- | | registerConversionDataCallback | Set a listener for the [GCD](https://dev.appsflyer.com/hc/docs/conversion-data) response, it is also the callback used for the [Legacy deferred deeplinking](https://dev.appsflyer.com/hc/docs/android-legacy-apis#deferred-deep-linking) | | registerOnAppOpenAttributionCallback | Set a listener for the [Legacy direct deeplinking](https://dev.appsflyer.com/hc/docs/android-legacy-apis) response | -| registerOnDeepLinkingCallback | Set a listener for the [UDL](https://dev.appsflyer.com/hc/docs/unified-deep-linking-udl) response | \ No newline at end of file +| registerOnDeepLinkingCallback | Set a listener for the [UDL](https://dev.appsflyer.com/hc/docs/unified-deep-linking-udl) response | + +### startSdk +`startSDK()` +In version 6.13.0 of the appslfyer-flutter-plugin SDK we added the option of splitting between the initialization stage and start stage. All you need to do is add the property manualStart: true to the init object, and later call appsFlyer.startSdk() whenever you decide. If this property is set to false or doesn’t exist, the sdk will start after calling appsFlyer.initSdk(...). + +```dart + // SDK Options + final AppsFlyerOptions options = AppsFlyerOptions( + afDevKey: "", + appId: "", + showDebug: true, + timeToWaitForATTUserAuthorization: 15, + manualStart: true); + _appsflyerSdk = AppsflyerSdk(options); + + // Init of AppsFlyer SDK + _appsflyerSdk.initSdk( + registerConversionDataCallback: true, + registerOnAppOpenAttributionCallback: true, + registerOnDeepLinkingCallback: true); + + //Here we start the SDK + _appsflyerSdk.startSDK(); +``` diff --git a/example/.metadata b/example/.metadata index af46018..417ad2f 100644 --- a/example/.metadata +++ b/example/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - channel: stable + revision: "ef1af02aead6fe2414f3aafa5a61087b610e1332" + channel: "stable" project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: android - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: ios - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: linux - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: macos - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: web - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 - platform: windows - create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 - base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72 + create_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 + base_revision: ef1af02aead6fe2414f3aafa5a61087b610e1332 # User provided section diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 1322214..967b61a 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -26,6 +26,15 @@ + + + + + + + + @@ -34,3 +43,4 @@ android:value="2" /> + diff --git a/example/android/app/src/main/kotlin/com/appsflyer/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/appsflyer/example/MainActivity.kt new file mode 100644 index 0000000..7a353b6 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/appsflyer/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.appsflyer.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/build.gradle b/example/android/build.gradle index 58a8c74..713d7f6 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id deleted file mode 100644 index 8436dea..0000000 --- a/example/ios/Flutter/.last_build_id +++ /dev/null @@ -1 +0,0 @@ -e5c404366af8a41e0714eac45ad46fb8 \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f828bd2..7c56964 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -2,25 +2,25 @@ - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index e8efba1..ec97fc6 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index 399e934..c4855bf 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile index d207307..d97f17e 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -28,7 +28,13 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + use_frameworks! + use_modular_headers! + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ebc25a5..87131a0 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + + + - - diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index 530b833..f9b0d7c 100644 --- a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -2,8 +2,6 @@ - BuildSystemType - Latest PreviewsEnabled diff --git a/example/ios/Runner/AFSDKMock.h b/example/ios/Runner/AFSDKMock.h deleted file mode 100644 index 815e20b..0000000 --- a/example/ios/Runner/AFSDKMock.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// AFSDKMock.h -// Runner -// -// Created by Shahar Cohen on 12/09/2019. -// Copyright © 2019 The Chromium Authors. All rights reserved. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface AFSDKMock : NSObject - -@end - -NS_ASSUME_NONNULL_END diff --git a/example/ios/Runner/AFSDKMock.m b/example/ios/Runner/AFSDKMock.m deleted file mode 100644 index d3b145c..0000000 --- a/example/ios/Runner/AFSDKMock.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// AFSDKMock.m -// Runner -// -// Created by Shahar Cohen on 12/09/2019. -// Copyright © 2019 The Chromium Authors. All rights reserved. -// - -#import "AFSDKMock.h" - -@implementation AFSDKMock - -@end diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h deleted file mode 100644 index 36e21bb..0000000 --- a/example/ios/Runner/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 1b819ba..0000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,40 +0,0 @@ -#import "AppDelegate.h" -#import "GeneratedPluginRegistrant.h" -#import "AppsflyerSdkPlugin.h" -#import - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (void)applicationDidBecomeActive:(nonnull UIApplication *)application { - if (@available(iOS 14, *)) { - [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { - // native code here - }]; - } -} - -// Reports app open from a Universal Link for iOS 9 or above -- (BOOL) application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler { - [[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler]; - return YES; - } - -// Reports app open from deep link from apps which do not support Universal Links (Twitter) and for iOS8 and below - - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation { - [[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:annotation]; - return YES; - } - -// Reports app open from deep link for iOS 10 - - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url - options:(NSDictionary *) options { - [[AppsFlyerAttribution shared] handleOpenUrl:url options:options]; - return YES; - } - -@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 552f643..70693e4 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,5 @@ import UIKit import Flutter -import AppsFlyerLib -import AppTrackingTransparency @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -10,27 +8,6 @@ import AppTrackingTransparency didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) - AppsFlyerLib.shared().waitForATTUserAuthorization(timeoutInterval: 60) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - - override func applicationDidBecomeActive(_ application: UIApplication) { - if #available(iOS 14, *) { - ATTrackingManager.requestTrackingAuthorization { (status) in - switch status { - case .denied: - print("AuthorizationSatus is denied") - case .notDetermined: - print("AuthorizationSatus is notDetermined") - case .restricted: - print("AuthorizationSatus is restricted") - case .authorized: - print("AuthorizationSatus is authorized") - @unknown default: - fatalError("Invalid authorization status") - } - } - } - } } diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 3d43d11..dc9ada4 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 28c6bf0..7353c41 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 2ccbfd9..797d452 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index f091b6b..6ed2d93 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cde121..4cd7b00 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index d0ef06e..fe73094 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index dcdc230..321773c 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 2ccbfd9..797d452 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index c8f9ed8..502f463 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index a6d6b86..0ec3034 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index a6d6b86..0ec3034 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 75b2d16..e9f5fea 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index c4df70d..84ac32a 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 6a84f41..8953cba 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index d0e1f58..0467bf1 100644 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Info-Debug.plist b/example/ios/Runner/Info-Debug.plist deleted file mode 100644 index 9a6b4aa..0000000 --- a/example/ios/Runner/Info-Debug.plist +++ /dev/null @@ -1,62 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - appsflyer_sdk_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - scheme - - - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - NSBonjourServices - - _dartobservatory._tcp - - NSUserTrackingUsageDescription - This app requires collecting data for advertisers - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/example/ios/Runner/Info-Release.plist b/example/ios/Runner/Info-Release.plist deleted file mode 100644 index 1ce6908..0000000 --- a/example/ios/Runner/Info-Release.plist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - appsflyer_sdk_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - scheme - - - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - NSUserTrackingUsageDescription - This app requires collecting data for advertisers - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index c508934..f15383a 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -26,8 +26,6 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS - NSUserTrackingUsageDescription - hey UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName @@ -47,7 +45,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - diff --git a/example/ios/Runner/Runner.entitlements b/example/ios/Runner/Runner.entitlements index 59521d0..93aa1e9 100644 --- a/example/ios/Runner/Runner.entitlements +++ b/example/ios/Runner/Runner.entitlements @@ -4,8 +4,7 @@ com.apple.developer.associated-domains - applinks:flutter-subdomain.onelink.me - applinks:gabrieltest.onelink.me + applinks:flutterdp.onelink.me diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index dff6597..0000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/example/lib/home_container.dart b/example/lib/home_container.dart index 221b0b6..43d8d82 100644 --- a/example/lib/home_container.dart +++ b/example/lib/home_container.dart @@ -4,20 +4,23 @@ import './app_constants.dart'; import 'text_border.dart'; import 'utils.dart'; -// ignore: must_be_immutable class HomeContainer extends StatefulWidget { final Map onData; - final Future Function(String, Map) logEvent; + final Future Function(String, Map) logEvent; Object deepLinkData; - HomeContainer({this.onData, this.deepLinkData, this.logEvent}); + HomeContainer({ + required this.onData, + required this.deepLinkData, + required this.logEvent, + }); @override _HomeContainerState createState() => _HomeContainerState(); } class _HomeContainerState extends State { - final String eventName = "purchase"; + final String eventName = "Purchase Event"; final Map eventValues = { "af_content_id": "id123", @@ -25,91 +28,120 @@ class _HomeContainerState extends State { "af_revenue": "20" }; - String _logEventResponse = "No event have been sent"; + String _logEventResponse = "Awaiting event status"; @override Widget build(BuildContext context) { return SingleChildScrollView( child: Container( - child: Padding( - padding: EdgeInsets.all(AppConstants.CONTAINER_PADDING), - child: Column( - children: [ - Text( - "AF SDK", - style: TextStyle(fontWeight: FontWeight.bold), - ), - Padding( - padding: EdgeInsets.only(top: AppConstants.TOP_PADDING), - ), - TextBorder( - controller: TextEditingController( - text: widget.onData != null - ? Utils.formatJson(widget.onData) - : "No conversion data"), - labelText: "Conversion Data:", - ), - Padding( - padding: EdgeInsets.only(top: 12.0), + padding: const EdgeInsets.all(AppConstants.CONTAINER_PADDING), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.blueGrey, + width: 0.5 + ), + borderRadius: BorderRadius.circular(5), + ),child: Column( + children: [ + Text( + "APPSFLYER SDK", + style: TextStyle( + fontSize: 18, + color: Colors.black, + fontWeight: FontWeight.w500, ), - TextBorder( - controller: TextEditingController( - text: widget.deepLinkData != null ? - Utils.formatJson(widget.deepLinkData) : - "No Attribution data"), - labelText: "Attribution Data:", + ), + SizedBox(height: AppConstants.TOP_PADDING), + TextBorder( + controller: TextEditingController( + text: widget.onData.isNotEmpty + ? Utils.formatJson(widget.onData) + : "Waiting for conversion data...", ), - Padding( - padding: EdgeInsets.only(top: 12.0), + labelText: "CONVERSION DATA", + ), + SizedBox(height: 12.0), + TextBorder( + controller: TextEditingController( + text: widget.deepLinkData != null + ? Utils.formatJson(widget.deepLinkData) + : "Waiting for attribution data...", ), - Container( - padding: EdgeInsets.all(20.0), - decoration: BoxDecoration( - border: Border.all( - color: Colors.grey, - ), + labelText: "ATTRIBUTION DATA", + ), + ], + ), + ), + SizedBox(height: 12.0), + Container( + padding: EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.blueGrey, + width: 0.5 ), - child: Column(children: [ - Center( - child: Text("Log event"), - ), - Padding( - padding: EdgeInsets.only(top: 12.0), + borderRadius: BorderRadius.circular(5), + ), + child: Column( + children: [ + Text( + "EVENT LOGGER", + style: TextStyle( + fontSize: 18, + color: Colors.black, + fontWeight: FontWeight.w500, + ), ), + SizedBox(height: 12.0), TextBorder( controller: TextEditingController( - text: - "event name: $eventName\nevent values: $eventValues"), - labelText: "Event Request", - ), - Padding( - padding: EdgeInsets.only(top: 12.0), + text: "Event Name: $eventName\nEvent Values: $eventValues" + ), + labelText: "EVENT REQUEST", ), + SizedBox(height: 12.0), TextBorder( - labelText: "Server response", - controller: - TextEditingController(text: _logEventResponse)), + labelText: "SERVER RESPONSE", + controller: TextEditingController( + text: _logEventResponse + ), + ), + SizedBox(height: 20.0), ElevatedButton( onPressed: () { - print("Pressed"); widget.logEvent(eventName, eventValues).then((onValue) { setState(() { - _logEventResponse = onValue.toString(); + _logEventResponse = "Event Status: " + onValue.toString(); }); }).catchError((onError) { setState(() { - _logEventResponse = onError.toString(); + _logEventResponse = "Error: " + onError.toString(); }); }); }, - child: Text("Send purchase event"), + child: Text("Trigger Purchase Event"), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + textStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), ), - ]), - ) - ], - ), + ], + ), + ) + ], ), ), ); } -} +} \ No newline at end of file diff --git a/example/lib/home_container_streams.dart b/example/lib/home_container_streams.dart index d9cab5c..b9c22d5 100644 --- a/example/lib/home_container_streams.dart +++ b/example/lib/home_container_streams.dart @@ -1,24 +1,26 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'app_constants.dart'; import 'text_border.dart'; import 'utils.dart'; - class HomeContainerStreams extends StatefulWidget { final Stream onData; final Stream onAttribution; final Future Function(String, Map) logEvent; - HomeContainerStreams({this.onData, this.onAttribution, this.logEvent}); + HomeContainerStreams({ + required this.onData, + required this.onAttribution, + required this.logEvent + }); @override _HomeContainerStreamsState createState() => _HomeContainerStreamsState(); } class _HomeContainerStreamsState extends State { - final String eventName = "my event"; + final String eventName = "Custom Event"; final Map eventValues = { "af_content_id": "id123", @@ -26,100 +28,123 @@ class _HomeContainerStreamsState extends State { "af_revenue": "2" }; - String _logEventResponse = "No event have been sent"; + String _logEventResponse = "Event status will be shown here once it's triggered."; @override Widget build(BuildContext context) { return SingleChildScrollView( child: Container( - child: Padding( - padding: EdgeInsets.all(AppConstants.CONTAINER_PADDING), - child: Column( - children: [ - Text( - "AF SDK", - style: TextStyle(fontWeight: FontWeight.bold), - ), - Padding( - padding: EdgeInsets.only(top: AppConstants.TOP_PADDING), - ), - StreamBuilder( - stream: widget.onData?.asBroadcastStream(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - return TextBorder( - controller: TextEditingController( - text: snapshot.hasData - ? Utils.formatJson(snapshot.data) - : "No conversion data"), - labelText: "Conversion Data:", - ); - }), - Padding( - padding: EdgeInsets.only(top: 12.0), - ), - StreamBuilder( - stream: widget.onAttribution?.asBroadcastStream(), - builder: - (BuildContext context, AsyncSnapshot snapshot) { - return TextBorder( - controller: TextEditingController( - text: snapshot.hasData - ? Utils.formatJson(snapshot.data) - : "No attribution data"), - labelText: "Attribution Data:", - ); - }), - Padding( - padding: EdgeInsets.only(top: 12.0), - ), - Container( + padding: EdgeInsets.all(AppConstants.CONTAINER_PADDING), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( padding: EdgeInsets.all(20.0), decoration: BoxDecoration( + color: Colors.white, border: Border.all( - color: Colors.grey, + color: Colors.blueGrey, + width: 0.5 ), + borderRadius: BorderRadius.circular(5), ), - child: Column(children: [ - Center( - child: Text("Log event"), - ), - Padding( - padding: EdgeInsets.only(top: 12.0), - ), - TextBorder( - controller: TextEditingController( - text: - "event name: $eventName\nevent values: $eventValues"), - labelText: "Event Request", + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "APPSFLYER SDK", + style: TextStyle( + fontSize: 18, + color: Colors.black, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(height: AppConstants.TOP_PADDING), + StreamBuilder( + stream: widget.onData.asBroadcastStream(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return TextBorder( + controller: TextEditingController( + text: snapshot.hasData ? Utils.formatJson(snapshot.data) : "Waiting for conversion data..." + ), + labelText: "CONVERSION DATA", + ); + }, + ), + SizedBox(height: 12.0), + StreamBuilder( + stream: widget.onAttribution.asBroadcastStream(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return TextBorder( + controller: TextEditingController( + text: snapshot.hasData ? Utils.formatJson(snapshot.data) : "Waiting for attribution data..." + ), + labelText: "ATTRIBUTION DATA", + ); + } + ), + ], + ) + ), + SizedBox(height: 12.0), + Container( + padding: EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: Colors.grey, + width: 0.5 + ), + ), + child: Column(children: [ + Text( + "EVENT LOGGER", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, ), - Padding( - padding: EdgeInsets.only(top: 12.0), + ), + SizedBox(height: 12.0), + TextBorder( + controller: TextEditingController( + text: "Event Name: $eventName\nEvent Values: $eventValues" ), - TextBorder( - labelText: "Server response", - controller: - TextEditingController(text: _logEventResponse)), - ElevatedButton( - onPressed: () { - widget.logEvent(eventName, eventValues).then((onValue) { - setState(() { - _logEventResponse = onValue.toString(); - }); - }).catchError((onError) { - setState(() { - _logEventResponse = onError.toString(); - }); + labelText: "EVENT REQUEST", + ), + SizedBox(height: 12.0), + TextBorder( + labelText: "SERVER RESPONSE", + controller: TextEditingController(text: _logEventResponse), + ), + SizedBox(height: 20), + ElevatedButton( + onPressed: () { + widget.logEvent(eventName, eventValues).then((onValue) { + setState(() { + _logEventResponse = onValue.toString(); + }); + }).catchError((onError) { + setState(() { + _logEventResponse = onError.toString(); }); - }, - child: Text("Send event"), + }); + }, + child: Text("Trigger Event"), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + textStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), ), - ]), - ) - ], - ), + ), + ]), + ) + ], ), ), ); } -} +} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 72ea3ca..a3e17ee 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import './main_page.dart'; Future main() async { - await DotEnv().load('.env'); + await dotenv.load(fileName: '.env'); runApp(MyApp()); } diff --git a/example/lib/main_page.dart b/example/lib/main_page.dart index 13c63ad..ddae0b9 100644 --- a/example/lib/main_page.dart +++ b/example/lib/main_page.dart @@ -13,32 +13,57 @@ class MainPage extends StatefulWidget { } class MainPageState extends State { - AppsflyerSdk _appsflyerSdk; - Map _deepLinkData; - Map _gcd; + late AppsflyerSdk _appsflyerSdk; + Map _deepLinkData = {}; + Map _gcd = {}; - // called on every foreground @override void initState() { super.initState(); + afStart(); + } + + void afStart() async { + // SDK Options final AppsFlyerOptions options = AppsFlyerOptions( - afDevKey: DotEnv().env["DEV_KEY"], - appId: DotEnv().env["APP_ID"], + afDevKey: dotenv.env["DEV_KEY"]!, + appId: dotenv.env["APP_ID"]!, showDebug: true, - timeToWaitForATTUserAuthorization: 15); + timeToWaitForATTUserAuthorization: 15, + manualStart: true); _appsflyerSdk = AppsflyerSdk(options); - _appsflyerSdk.onAppOpenAttribution((res) { - print("onAppOpenAttribution res: " + res.toString()); - setState(() { - _deepLinkData = res; - }); - }); + + //Setting configuration to the SDK + _appsflyerSdk.setCurrencyCode("USD"); + _appsflyerSdk.enableTCFDataCollection(true); + // var forGdpr = _appsflyerSdk.forGDPRUser(hasConsentForDataUsage: true, hasConsentForAdsPersonalization: true); + // _appsflyerSdk.setConsentData(forGdpr); + var nonGdpr = _appsflyerSdk.nonGDPRUser(); + _appsflyerSdk.setConsentData(nonGdpr); + + // Init of AppsFlyer SDK + await _appsflyerSdk.initSdk( + registerConversionDataCallback: true, + registerOnAppOpenAttributionCallback: true, + registerOnDeepLinkingCallback: true); + + // Conversion data callback _appsflyerSdk.onInstallConversionData((res) { print("onInstallConversionData res: " + res.toString()); setState(() { _gcd = res; }); }); + + // App open attribution callback + _appsflyerSdk.onAppOpenAttribution((res) { + print("onAppOpenAttribution res: " + res.toString()); + setState(() { + _deepLinkData = res; + }); + }); + + // Deep linking callback _appsflyerSdk.onDeepLinking((DeepLinkResult dp) { switch (dp.status) { case Status.FOUND: @@ -60,45 +85,52 @@ class MainPageState extends State { _deepLinkData = dp.toJson(); }); }); + + setState(() {}); // Call setState to rebuild the widget } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Column( - children: [ - Text('AppsFlyer SDK example app'), - FutureBuilder( - builder: (BuildContext context, AsyncSnapshot snapshot) { - return Text(snapshot.hasData ? snapshot.data : ""); - }), - ], - ), - ), - body: FutureBuilder( - future: _appsflyerSdk.initSdk( - registerConversionDataCallback: true, - registerOnAppOpenAttributionCallback: false, - registerOnDeepLinkingCallback: true), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Center(child: CircularProgressIndicator()); - } else { - if (snapshot.hasData) { - return HomeContainer( + appBar: AppBar( + title: Text('AppsFlyer SDK example app'), + centerTitle: true, + backgroundColor: Colors.green, + ), + body: Builder( + builder: (context) { + return SafeArea( + child: Column( + children: [ + Expanded( + child: HomeContainer( onData: _gcd, deepLinkData: _deepLinkData, logEvent: logEvent, - ); - } else { - return Center(child: Text("Error initializing sdk")); - } - } - })); + ), + ), + ElevatedButton( + onPressed: () { + _appsflyerSdk.startSDK(); + }, + child: Text("START SDK"), + ) + ], + ), + ); + }, + ), + ); } - Future logEvent(String eventName, Map eventValues) { - return _appsflyerSdk.logEvent(eventName, eventValues); + Future logEvent(String eventName, Map eventValues) async { + bool? logResult; + try { + logResult = await _appsflyerSdk.logEvent(eventName, eventValues); + print("Event logged"); + } catch (e) { + print("Failed to log event: $e"); + } + return logResult; } } diff --git a/example/lib/text_border.dart b/example/lib/text_border.dart index 87cf05c..6643352 100644 --- a/example/lib/text_border.dart +++ b/example/lib/text_border.dart @@ -2,23 +2,38 @@ import 'package:flutter/material.dart'; // ignore: must_be_immutable class TextBorder extends StatelessWidget { - TextEditingController controller; - String labelText; + final TextEditingController controller; + final String labelText; - TextBorder({this.controller, this.labelText}); + const TextBorder({ + required this.controller, + required this.labelText, + Key? key + }) : super(key: key); @override Widget build(BuildContext context) { - return TextField( - controller: controller, - enabled: false, - maxLines: null, - decoration: InputDecoration( - labelText: labelText, - border: OutlineInputBorder( - borderSide: BorderSide(width: 0.0), + return Container( + margin: const EdgeInsets.symmetric(vertical: 8.0), // Add some vertical margin + child: TextField( + controller: controller, + enabled: false, + maxLines: null, + decoration: InputDecoration( + labelText: labelText, + labelStyle: TextStyle(color: Colors.blueGrey), // Change the color of the label + border: OutlineInputBorder( + borderSide: BorderSide( + width: 1.0 + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + width: 1.0 + ), + ), ), ), ); } -} +} \ No newline at end of file diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e3ac809..fc8901f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,7 +12,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: ">=2.0.0 <3.0.0" + sdk: '>=2.12.0 <4.0.0' dependencies: flutter: @@ -22,8 +22,8 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - flutter_dotenv: ^2.1.0 + cupertino_icons: ^1.0.6 + flutter_dotenv: ^5.1.0 dev_dependencies: flutter_test: diff --git a/ios/Classes/AppsflyerSdkPlugin.h b/ios/Classes/AppsflyerSdkPlugin.h index cb5ba0c..0e2a9dc 100644 --- a/ios/Classes/AppsflyerSdkPlugin.h +++ b/ios/Classes/AppsflyerSdkPlugin.h @@ -8,6 +8,8 @@ @interface AppsflyerSdkPlugin: NSObject +@property (readwrite, nonatomic) BOOL isManualStart; + + (FlutterMethodChannel*)callbackChannel; + (BOOL)gcdCallback; + (BOOL)oaoaCallback; @@ -16,10 +18,11 @@ @end // Appsflyer JS objects -#define kAppsFlyerPluginVersion @"6.12.2" +#define kAppsFlyerPluginVersion @"6.13.0" #define afDevKey @"afDevKey" #define afAppId @"afAppId" #define afIsDebug @"isDebug" +#define afManualStart @"manualStart" #define afTimeToWaitForATTUserAuthorization @"timeToWaitForATTUserAuthorization" #define afEventName @"eventName" #define afEventValues @"eventValues" diff --git a/ios/Classes/AppsflyerSdkPlugin.m b/ios/Classes/AppsflyerSdkPlugin.m index 7ab2cfa..8183e37 100644 --- a/ios/Classes/AppsflyerSdkPlugin.m +++ b/ios/Classes/AppsflyerSdkPlugin.m @@ -67,15 +67,15 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self initSdkWithCall:call result:result]; }else if([@"getSDKVersion" isEqualToString:call.method]){ [self getSDKVersion:result]; - } - else if([@"logEvent" isEqualToString:call.method]){ + }else if([@"startSDK" isEqualToString:call.method]){ + [self startSDK:call result:result]; + } else if([@"logEvent" isEqualToString:call.method]){ [self logEventWithCall:call result:result]; }else if([@"waitForCustomerUserId" isEqualToString:call.method]){ [self waitForCustomerId:call result:result]; }else if([@"setUserEmails" isEqualToString:call.method]){ [self setUserEmails:call result:result]; - } - else if([@"updateServerUninstallToken" isEqualToString:call.method]){ + }else if([@"updateServerUninstallToken" isEqualToString:call.method]){ [self updateServerUninstallToken:call result:result]; }else if([@"enableUninstallTracking" isEqualToString:call.method]){ // @@ -90,7 +90,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { }else if([@"setCustomerIdAndLogSession" isEqualToString:call.method]){ [self setCustomerUserId:call result:result]; }else if([@"setCurrencyCode" isEqualToString:call.method ]){ - // + [self setCurrencyCode:call result:result]; }else if([@"setMinTimeBetweenSessions" isEqualToString:call.method]){ [self setMinTimeBetweenSessions:call result:result]; }else if([@"getHostPrefix" isEqualToString:call.method]){ @@ -143,12 +143,46 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self setResolveDeepLinkURLs:call result:result]; }else if([@"addPushNotificationDeepLinkPath" isEqualToString:call.method]){ [self addPushNotificationDeepLinkPath:call result:result]; + }else if([@"enableTCFDataCollection" isEqualToString:call.method]){ + [self enableTCFDataCollection:call result:result]; + }else if([@"setConsentData" isEqualToString:call.method]){ + [self setConsentData:call result:result]; } else{ result(FlutterMethodNotImplemented); } } +-(void)startSDK:(FlutterMethodCall*)call result:(FlutterResult)result { + [[AppsFlyerLib shared] start]; + result(nil); +} + +- (void)setConsentData:(FlutterMethodCall*)call result:(FlutterResult)result { + NSDictionary* consentDict = call.arguments[@"consentData"]; + + BOOL isUserSubjectToGDPR = [consentDict[@"isUserSubjectToGDPR"] boolValue]; + BOOL hasConsentForDataUsage = [consentDict[@"hasConsentForDataUsage"] boolValue]; + BOOL hasConsentForAdsPersonalization = [consentDict[@"hasConsentForAdsPersonalization"] boolValue]; + + AppsFlyerConsent *consentData; + if(isUserSubjectToGDPR){ + consentData = [[AppsFlyerConsent alloc] initForGDPRUserWithHasConsentForDataUsage:hasConsentForDataUsage + hasConsentForAdsPersonalization:hasConsentForAdsPersonalization]; + }else{ + consentData = [[AppsFlyerConsent alloc] initNonGDPRUser]; + } + + [[AppsFlyerLib shared] setConsentData:consentData]; + result(nil); +} + +- (void)enableTCFDataCollection:(FlutterMethodCall*)call result:(FlutterResult)result { + BOOL shouldCollect = [call.arguments[@"shouldCollect"] boolValue]; + [[AppsFlyerLib shared] enableTCFDataCollection:shouldCollect]; + result(nil); +} + - (void)addPushNotificationDeepLinkPath:(FlutterMethodCall*)call result:(FlutterResult)result{ NSArray* deeplinkPath = call.arguments; if(deeplinkPath != nil){ @@ -463,6 +497,12 @@ - (void)setCustomerUserId:(FlutterMethodCall*)call result:(FlutterResult)result{ result(nil); } +- (void)setCurrencyCode:(FlutterMethodCall*)call result:(FlutterResult)result{ + NSString* currencyCode = call.arguments[@"currencyCode"]; + [[AppsFlyerLib shared] setCurrencyCode:currencyCode]; + result(nil); +} + - (void)stop:(FlutterMethodCall*)call result:(FlutterResult)result{ BOOL stop = [[call.arguments objectForKey:@"isStopped"] boolValue]; [AppsFlyerLib shared].isStopped = stop; @@ -508,6 +548,7 @@ - (void)initSdkWithCall:(FlutterMethodCall*)call result:(FlutterResult)result{ NSString* devKey = nil; NSString* appId = nil; NSString* appInviteOneLink = nil; + BOOL manualStart = NO; BOOL disableCollectASA = NO; BOOL disableAdvertisingIdentifier = NO; NSTimeInterval timeToWaitForATTUserAuthorization = 0; @@ -524,7 +565,9 @@ - (void)initSdkWithCall:(FlutterMethodCall*)call result:(FlutterResult)result{ devKey = call.arguments[afDevKey]; appId = call.arguments[afAppId]; timeToWaitForATTUserAuthorization = [(id)call.arguments[afTimeToWaitForATTUserAuthorization] doubleValue]; - + manualStart = call.arguments[afManualStart]; + [self setIsManualStart:manualStart]; + isDebugValue = call.arguments[afIsDebug]; if ([isDebugValue isKindOfClass:[NSNumber class]]) { // isDebug is a boolean that will come through as an NSNumber @@ -590,8 +633,15 @@ - (void)initSdkWithCall:(FlutterMethodCall*)call result:(FlutterResult)result{ [[AppsFlyerLib shared] waitForATTUserAuthorizationWithTimeoutInterval:timeToWaitForATTUserAuthorization]; } - [[AppsFlyerLib shared] start]; - + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(sendLaunch:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + if (!manualStart){ + [[AppsFlyerLib shared] start]; + } + //post notification for the deep link object that the bridge is set and he can handle deep link [AppsFlyerAttribution shared].isBridgeReady = YES; [[NSNotificationCenter defaultCenter] postNotificationName:AF_BRIDGE_SET object:self]; @@ -601,6 +651,12 @@ - (void)initSdkWithCall:(FlutterMethodCall*)call result:(FlutterResult)result{ result(@{@"status": @"OK"}); } +-(void)sendLaunch:(UIApplication *)application { + if (![self isManualStart]) { + [[AppsFlyerLib shared] start]; + } +} + -(void)logEventWithCall:(FlutterMethodCall*)call result:(FlutterResult)result{ NSString *eventName = call.arguments[afEventName]; NSDictionary *eventValues = call.arguments[afEventValues]; diff --git a/ios/appsflyer_sdk.podspec b/ios/appsflyer_sdk.podspec index c935bcd..f0208bf 100644 --- a/ios/appsflyer_sdk.podspec +++ b/ios/appsflyer_sdk.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'appsflyer_sdk' - s.version = '6.8.0' + s.version = '6.13.0' s.summary = 'AppsFlyer Integration for Flutter' s.description = <<-DESC AppsFlyer is the market leader in mobile advertising attribution & analytics, helping marketers to pinpoint their targeting, optimize their ad spend and boost their ROI. @@ -21,6 +21,6 @@ AppsFlyer is the market leader in mobile advertising attribution & analytics, he s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.ios.dependency 'AppsFlyerFramework', '6.12.2' + s.ios.dependency 'AppsFlyerFramework', '6.13.0' end diff --git a/lib/appsflyer_sdk.dart b/lib/appsflyer_sdk.dart index 12de504..e95fed9 100644 --- a/lib/appsflyer_sdk.dart +++ b/lib/appsflyer_sdk.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'src/appsflyer_consent.dart'; import 'src/callbacks.dart'; part 'src/appsflyer_constants.dart'; diff --git a/lib/src/appsflyer_consent.dart b/lib/src/appsflyer_consent.dart new file mode 100644 index 0000000..5631a71 --- /dev/null +++ b/lib/src/appsflyer_consent.dart @@ -0,0 +1,40 @@ +class AppsFlyerConsent { + final bool isUserSubjectToGDPR; + final bool hasConsentForDataUsage; + final bool hasConsentForAdsPersonalization; + + AppsFlyerConsent._({ + required this.isUserSubjectToGDPR, + required this.hasConsentForDataUsage, + required this.hasConsentForAdsPersonalization, + }); + + // Factory constructors + factory AppsFlyerConsent.forGDPRUser({ + required bool hasConsentForDataUsage, + required bool hasConsentForAdsPersonalization + }){ + return AppsFlyerConsent._( + isUserSubjectToGDPR: true, + hasConsentForDataUsage: hasConsentForDataUsage, + hasConsentForAdsPersonalization: hasConsentForAdsPersonalization + ); + } + + factory AppsFlyerConsent.nonGDPRUser(){ + return AppsFlyerConsent._( + isUserSubjectToGDPR: false, + hasConsentForDataUsage: false, + hasConsentForAdsPersonalization: false + ); + } + + // Converts object to a map + Map toMap() { + return { + 'isUserSubjectToGDPR': isUserSubjectToGDPR, + 'hasConsentForDataUsage': hasConsentForDataUsage, + 'hasConsentForAdsPersonalization': hasConsentForAdsPersonalization, + }; + } +} \ No newline at end of file diff --git a/lib/src/appsflyer_constants.dart b/lib/src/appsflyer_constants.dart index 3b33787..0ee155e 100644 --- a/lib/src/appsflyer_constants.dart +++ b/lib/src/appsflyer_constants.dart @@ -9,6 +9,7 @@ class AppsflyerConstants { static const String AF_DEV_KEY = "afDevKey"; static const String AF_APP_Id = "afAppId"; static const String AF_IS_DEBUG = "isDebug"; + static const String AF_MANUAL_START = "manualStart"; static const String AF_TIME_TO_WAIT_FOR_ATT_USER_AUTHORIZATION = "timeToWaitForATTUserAuthorization"; static const String AF_GCD = "GCD"; diff --git a/lib/src/appsflyer_options.dart b/lib/src/appsflyer_options.dart index 8d68201..2e6c66b 100644 --- a/lib/src/appsflyer_options.dart +++ b/lib/src/appsflyer_options.dart @@ -9,11 +9,13 @@ class AppsFlyerOptions { final String? appInviteOneLink; final bool? disableAdvertisingIdentifier; final bool? disableCollectASA; + final bool? manualStart; /// Creates an [AppsFlyerOptions] instance. /// Requires [afDevKey] and [appId] as mandatory Named parameters. /// All other parameters are optional, it's allows greater flexibility /// when invoking the constructor. + /// When manual start is true the startSDK must be called AppsFlyerOptions({ required this.afDevKey, this.showDebug = false, @@ -22,5 +24,6 @@ class AppsFlyerOptions { this.appInviteOneLink, this.disableAdvertisingIdentifier, this.disableCollectASA, + this.manualStart = false, }); } diff --git a/lib/src/appsflyer_sdk.dart b/lib/src/appsflyer_sdk.dart index 09e167d..c11b382 100644 --- a/lib/src/appsflyer_sdk.dart +++ b/lib/src/appsflyer_sdk.dart @@ -31,6 +31,16 @@ class AppsflyerSdk { return _instance!; } + AppsFlyerConsent forGDPRUser({required bool hasConsentForDataUsage, required bool hasConsentForAdsPersonalization}) { + return AppsFlyerConsent.forGDPRUser( + hasConsentForDataUsage: hasConsentForDataUsage, + hasConsentForAdsPersonalization: hasConsentForAdsPersonalization); + } + + AppsFlyerConsent nonGDPRUser() { + return AppsFlyerConsent.nonGDPRUser(); + } + @visibleForTesting AppsflyerSdk.private(this._methodChannel, this._eventChannel, {this.afOptions, this.mapOptions}); @@ -39,6 +49,9 @@ class AppsflyerSdk { Map _validateAFOptions(AppsFlyerOptions options) { Map validatedOptions = {}; + bool? manualStart = options.manualStart; + validatedOptions[AppsflyerConstants.AF_MANUAL_START] = manualStart; + //validations dynamic devKey = options.afDevKey; assert(devKey != null); @@ -172,6 +185,10 @@ class AppsflyerSdk { }); } + void startSDK(){ + _methodChannel.invokeMethod("startSDK"); + } + /// Retrieves the current SDK version. Future getSDKVersion() async { return _methodChannel.invokeMethod("getSDKVersion"); @@ -246,6 +263,15 @@ class AppsflyerSdk { .invokeMethod("setCurrencyCode", {'currencyCode': currencyCode}); } + /// Setting whether the SDK should collect tcf data automatically from SharedPreferences/UserDefaults + void enableTCFDataCollection(bool shouldCollect){ + _methodChannel.invokeListMethod("enableTCFDataCollection", {'shouldCollect': shouldCollect}); + } + + void setConsentData(AppsFlyerConsent consentData) { + _methodChannel.invokeMethod('setConsentData', {'consentData': consentData.toMap()}); + } + /// Setting your own customer ID enables you to cross-reference your own unique ID with AppsFlyer’s unique ID and the other devices’ IDs. /// This ID is available in AppsFlyer CSV reports along with Postback APIs for cross-referencing with your internal IDs. void setCustomerUserId(String id) { diff --git a/pubspec.yaml b/pubspec.yaml index ba35a38..8a5d8f1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: appsflyer_sdk description: A Flutter plugin for AppsFlyer SDK. Supports iOS and Android. -version: 6.12.2 +version: 6.13.0 homepage: https://github.com/AppsFlyerSDK/flutter_appsflyer_sdk environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' flutter: ">=1.10.0" dependencies: @@ -16,7 +16,7 @@ dev_dependencies: flutter_test: sdk: flutter test: ^1.16.5 - mockito: 5.2.0 + mockito: ^5.4.4 effective_dart: ^1.3.0