From 29527df3fa123cfe38a097d0033c5ae723b468cf Mon Sep 17 00:00:00 2001 From: uerceg Date: Thu, 26 Jun 2025 14:02:56 +0200 Subject: [PATCH 01/17] feat: update version number to 5.4.1 --- VERSION | 2 +- android/build.gradle | 2 +- ios/adjust_sdk.podspec | 4 ++-- lib/adjust.dart | 2 +- pubspec.yaml | 2 +- test/ios/test_lib.podspec | 2 +- test/pubspec.yaml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 8a30e8f..ade6522 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.4.0 +5.4.1 diff --git a/android/build.gradle b/android/build.gradle index 7577056..7048a97 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -37,5 +37,5 @@ android { } dependencies { - implementation 'com.adjust.sdk:adjust-android:5.4.0' + implementation 'com.adjust.sdk:adjust-android:5.4.1' } diff --git a/ios/adjust_sdk.podspec b/ios/adjust_sdk.podspec index ecba12c..9847be2 100644 --- a/ios/adjust_sdk.podspec +++ b/ios/adjust_sdk.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'adjust_sdk' - s.version = '5.4.0' + s.version = '5.4.1' s.summary = 'Adjust Flutter SDK for iOS platform' s.description = <<-DESC Adjust Flutter SDK for iOS platform. @@ -14,5 +14,5 @@ Pod::Spec.new do |s| s.ios.deployment_target = '12.0' s.dependency 'Flutter' - s.dependency 'Adjust', '5.4.0' + s.dependency 'Adjust', '5.4.1' end diff --git a/lib/adjust.dart b/lib/adjust.dart index 52f37e0..44980d2 100644 --- a/lib/adjust.dart +++ b/lib/adjust.dart @@ -24,7 +24,7 @@ import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; class Adjust { - static const String _sdkPrefix = 'flutter5.4.0'; + static const String _sdkPrefix = 'flutter5.4.1'; static const MethodChannel _channel = const MethodChannel('com.adjust.sdk/api'); diff --git a/pubspec.yaml b/pubspec.yaml index 5e1780f..2ccf7bc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: adjust_sdk description: This is the Flutter SDK of Adjust™. You can read more about Adjust™ at adjust.com. homepage: https://github.com/adjust/flutter_sdk -version: 5.4.0 +version: 5.4.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/test/ios/test_lib.podspec b/test/ios/test_lib.podspec index d5b9efc..ec1c972 100644 --- a/test/ios/test_lib.podspec +++ b/test/ios/test_lib.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'test_lib' - s.version = '5.4.0' + s.version = '5.4.1' s.summary = 'Adjust test library for iOS platform' s.description = <<-DESC Adjust test library for iOS platform. diff --git a/test/pubspec.yaml b/test/pubspec.yaml index 521c9b4..7174598 100644 --- a/test/pubspec.yaml +++ b/test/pubspec.yaml @@ -1,6 +1,6 @@ name: test_lib description: Flutter plugin for Adjust Testing Library. Intended exclusively for internal use. -version: 5.4.0 +version: 5.4.1 author: Adjust (sdk@adjust.com) environment: From 0859dbd1ad1ede260d8568fca72359bb70abb33e Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 08:16:28 +0200 Subject: [PATCH 02/17] refac: update example and test apps --- android/build.gradle | 2 +- example/android/app/build.gradle | 8 ++++++-- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Podfile | 5 ++++- example/ios/Runner.xcodeproj/project.pbxproj | 12 ++++++------ .../xcshareddata/xcschemes/Runner.xcscheme | 2 ++ test/android/build.gradle | 2 +- test/app/android/app/build.gradle | 9 +++++++++ test/app/android/settings.gradle | 2 +- test/app/ios/Flutter/AppFrameworkInfo.plist | 2 +- test/app/ios/Podfile | 2 +- test/app/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- .../xcshareddata/xcschemes/Runner.xcscheme | 2 ++ test/app/lib/main.dart | 4 ++-- 14 files changed, 40 insertions(+), 20 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 7048a97..fc6f042 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath('com.android.tools.build:gradle:8.1.4') + classpath('com.android.tools.build:gradle:8.2.1') } } rootProject.allprojects { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 6822fae..2258cd6 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -28,8 +28,12 @@ android { compileSdk = 34 compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_18 + targetCompatibility JavaVersion.VERSION_18 + } + + kotlinOptions { + jvmTarget = "18" } defaultConfig { diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8c6e561..d57061d 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 2c068c4..c0792a2 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, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -31,6 +31,9 @@ target 'Runner' do use_frameworks! use_modular_headers! + pod 'Adjust/AdjustGoogleOdm' + pod 'GoogleAdsOnDeviceConversion', '2.0.0' + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index ca29ecb..4dd5d25 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -365,7 +365,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; @@ -389,7 +389,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -454,7 +454,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -504,7 +504,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; @@ -530,7 +530,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -563,7 +563,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 4590184..ae420c9 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/test/app/ios/Podfile b/test/app/ios/Podfile index 1535fd8..dc9af58 100644 --- a/test/app/ios/Podfile +++ b/test/app/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/test/app/ios/Runner.xcodeproj/project.pbxproj b/test/app/ios/Runner.xcodeproj/project.pbxproj index ab4fddb..264206e 100644 --- a/test/app/ios/Runner.xcodeproj/project.pbxproj +++ b/test/app/ios/Runner.xcodeproj/project.pbxproj @@ -363,7 +363,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -467,7 +467,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -517,7 +517,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/test/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c53e2b3..9c12df5 100644 --- a/test/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/test/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> { super.initState(); if (Platform.isAndroid) { - String _address = '192.168.86.227'; + String _address = '192.168.8.78'; String _protocol = 'https'; String _port = '8443'; _overwriteUrl = _protocol + '://' + _address + ':' + _port; _controlUrl = 'ws://' + _address + ':1987'; } else { - String _address = '192.168.86.227'; + String _address = '192.168.8.78'; String _protocol = 'http'; String _port = '8080'; _overwriteUrl = _protocol + '://' + _address + ':' + _port; From 6f5a3e79f36a935b85c7a9ffd5f857892cd04594 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 10:10:38 +0200 Subject: [PATCH 03/17] refac: rebuild example app --- .gitignore | 3 +- example/.gitignore | 109 +-- example/.metadata | 32 +- example/README.md | 15 +- example/android/.gitignore | 7 +- example/android/app/build.gradle | 62 -- example/android/app/build.gradle.kts | 44 ++ .../android/app/src/debug/AndroidManifest.xml | 3 +- .../android/app/src/main/AndroidManifest.xml | 40 +- .../com/adjust/examples/MainActivity.kt | 21 +- .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 3 +- example/android/build.gradle | 31 - example/android/build.gradle.kts | 24 + example/android/gradle.properties | 5 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- example/android/gradlew | 160 +++++ example/android/gradlew.bat | 90 +++ example/android/settings.gradle | 25 - example/android/settings.gradle.kts | 26 + example/ios/.gitignore | 3 + example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Flutter/Debug.xcconfig | 2 +- example/ios/Flutter/Release.xcconfig | 2 +- example/ios/Podfile | 9 +- example/ios/Runner.xcodeproj/project.pbxproj | 304 ++++++--- .../xcshareddata/xcschemes/Runner.xcscheme | 13 +- example/ios/Runner/AppDelegate.swift | 26 +- .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 1418 bytes example/ios/Runner/Info.plist | 30 +- example/ios/Runner/Runner.entitlements | 12 - example/lib/main.dart | 632 +++++++++--------- example/lib/util.dart | 119 ---- example/pubspec.yaml | 38 +- example/test/widget_test.dart | 30 - 49 files changed, 1021 insertions(+), 935 deletions(-) delete mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/build.gradle.kts create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/values-night/styles.xml delete mode 100644 example/android/build.gradle create mode 100644 example/android/build.gradle.kts create mode 100755 example/android/gradlew create mode 100644 example/android/gradlew.bat delete mode 100644 example/android/settings.gradle create mode 100644 example/android/settings.gradle.kts delete mode 100644 example/ios/Runner/Runner.entitlements delete mode 100644 example/lib/util.dart delete mode 100644 example/test/widget_test.dart diff --git a/.gitignore b/.gitignore index 0e23047..8e123bb 100644 --- a/.gitignore +++ b/.gitignore @@ -62,8 +62,6 @@ unlinked_spec.ds **/android/**/gradle-wrapper.jar **/android/.gradle **/android/captures/ -**/android/gradlew -**/android/gradlew.bat **/android/local.properties **/android/**/GeneratedPluginRegistrant.java **/android/key.properties @@ -83,6 +81,7 @@ unlinked_spec.ds **/ios/**/Icon? **/ios/**/Pods/ **/ios/**/.symlinks/ +**/ios/Podfile.lock **/ios/**/profile **/ios/**/xcuserdata **/ios/.generated/ diff --git a/example/.gitignore b/example/.gitignore index 2bead7e..3820a95 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,14 +1,16 @@ # Miscellaneous *.class -*.lock *.log *.pyc *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ +migrate_working_dir/ # IntelliJ related *.iml @@ -16,103 +18,28 @@ *.iws .idea/ -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/ - -# Flutter repo-specific -/bin/cache/ -/bin/internal/bootstrap.bat -/bin/internal/bootstrap.sh -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/devicelab/ABresults*.json -/dev/docs/doc/ -/dev/docs/flutter.docs.zip -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/dev/integration_tests/**/xcuserdata -/dev/integration_tests/**/Pods -/packages/flutter/coverage/ -version -analysis_benchmark.json - -# packages file containing multi-root paths -.packages.generated +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ -.flutter-plugins .flutter-plugins-dependencies -**/generated_plugin_registrant.dart -.packages .pub-cache/ .pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds +/build/ +/coverage/ -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/key.properties -*.jks - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/.last_build_id -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/ephemeral -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* - -# macOS -**/macos/Flutter/GeneratedPluginRegistrant.swift - -# Coverage -coverage/ - -# Symbols +# Symbolication related app.*.symbols -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock \ No newline at end of file +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata index 6eb54a1..6bbd235 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,8 +4,8 @@ # This file should be version controlled and should not be manually edited. version: - revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" - channel: "stable" + revision: "961ce3ec82ab88237be012c24eee24f9b10591dd" + channel: "master" project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: android - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: ios - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: linux - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: macos - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: web - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd - platform: windows - create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 - base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + create_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd + base_revision: 961ce3ec82ab88237be012c24eee24f9b10591dd # User provided section diff --git a/example/README.md b/example/README.md index aeaccb6..1b7a4e3 100644 --- a/example/README.md +++ b/example/README.md @@ -1,16 +1,3 @@ # example -Adjust Flutter Applicаtion - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +A new Flutter project. diff --git a/example/android/.gitignore b/example/android/.gitignore index 0a741cb..da7bcd2 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -1,11 +1,12 @@ gradle-wrapper.jar /.gradle /captures/ -/gradlew -/gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +# See https://flutter.dev/to/reference-keystore key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 2258cd6..0000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file("local.properties") -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader("UTF-8") { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty("flutter.versionCode") -if (flutterVersionCode == null) { - flutterVersionCode = "1" -} - -def flutterVersionName = localProperties.getProperty("flutter.versionName") -if (flutterVersionName == null) { - flutterVersionName = "1.0" -} - -android { - namespace = "com.adjust.examples" - compileSdk = 34 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_18 - targetCompatibility JavaVersion.VERSION_18 - } - - kotlinOptions { - jvmTarget = "18" - } - - defaultConfig { - applicationId "com.adjust.examples" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 34 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug - } - } -} - -flutter { - source = "../.." -} - -dependencies { - implementation 'com.adjust.sdk:adjust-android:5.4.0' -} \ No newline at end of file diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 0000000..d16e79d --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.adjust.examples" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.adjust.examples" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index f880684..399f698 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,6 @@ - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 9802ea3..842f600 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,20 +1,17 @@ - + android:windowSoftInputMode="adjustResize"> - - - - - - - - @@ -50,4 +31,15 @@ android:name="flutterEmbedding" android:value="2" /> + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt b/example/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt index 17c8b39..582cb5a 100644 --- a/example/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt @@ -1,24 +1,5 @@ package com.adjust.examples -import android.content.Intent -import android.os.Bundle -import com.adjust.sdk.Adjust -import com.adjust.sdk.AdjustDeeplink import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity(){ - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val intent: Intent = intent - val data = intent.data - Adjust.processDeeplink(AdjustDeeplink(data), this) - } - - override fun onNewIntent(intent: Intent) { - super.onNewIntent(intent) - val data = intent?.data - Adjust.processDeeplink(AdjustDeeplink(data), this) - } - - -} +class MainActivity : FlutterActivity() \ No newline at end of file diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index f880684..399f698 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,6 @@ - diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index bb91beb..0000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.8.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 5f5d39d..f018a61 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,6 +1,3 @@ -org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true -android.defaults.buildfeatures.buildconfig=true -android.nonTransitiveRClass=false -android.nonFinalResIds=false diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index df97d72..ac3b479 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip -networkTimeout=10000 -validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/example/android/gradlew b/example/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/example/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/example/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index de88743..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.8.0' apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false -} - -include ":app" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 0000000..fb605bc --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.9.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/example/ios/.gitignore b/example/ios/.gitignore index e96ef60..a2c66e5 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -11,6 +12,7 @@ Icon? **/Pods/ **/.symlinks/ +Podfile.lock profile xcuserdata **/.generated/ @@ -18,6 +20,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index d57061d..1dc6cf7 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier 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 c0792a2..620e46e 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, '13.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -29,12 +29,11 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! - use_modular_headers! - - pod 'Adjust/AdjustGoogleOdm' - pod 'GoogleAdsOnDeviceConversion', '2.0.0' 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/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 4dd5d25..7875912 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,16 +10,26 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 88F4A3FD2DC44C7129665E88 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - 9D934E032612994B00BDDA0B /* AdServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D934E022612994B00BDDA0B /* AdServices.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 9DF8055D24F9214A00A001CE /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF8055C24F9214900A001CE /* AdSupport.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 9DF8055F24F9215100A001CE /* AppTrackingTransparency.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF8055E24F9215100A001CE /* AppTrackingTransparency.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 9DF8056124F9215600A001CE /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF8056024F9215600A001CE /* StoreKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - B6025B3595AA1F9CF9F139AC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 356E070BF8DE437AF960ADE2 /* Pods_Runner.framework */; }; + 9DE448242E0E7D6C008D9604 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DE448232E0E7D6C008D9604 /* AdSupport.framework */; }; + 9DE448262E0E7D71008D9604 /* AdServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DE448252E0E7D71008D9604 /* AdServices.framework */; }; + 9DE448282E0E7D76008D9604 /* AppTrackingTransparency.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DE448272E0E7D76008D9604 /* AppTrackingTransparency.framework */; }; + 9DE4482A2E0E7D7A008D9604 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DE448292E0E7D7A008D9604 /* StoreKit.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -34,12 +44,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 08D505F93980F9E52B00EC1A /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 33F9C572C86811AA0C86097B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 356E070BF8DE437AF960ADE2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 303AB0B2394CD08FECD86B96 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5F31F7FF83E0ADA732B55E3B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 5B88428D631C6DEE5FAD69F8 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 5D79E5F82EB64B9CD0A679F8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -50,40 +61,49 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9D934E022612994B00BDDA0B /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; }; - 9DD8A2442DF887F700A682DE /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; - 9DF8055A24F9214500A001CE /* iAd.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = iAd.framework; path = System/Library/Frameworks/iAd.framework; sourceTree = SDKROOT; }; - 9DF8055C24F9214900A001CE /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; - 9DF8055E24F9215100A001CE /* AppTrackingTransparency.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppTrackingTransparency.framework; path = System/Library/Frameworks/AppTrackingTransparency.framework; sourceTree = SDKROOT; }; - 9DF8056024F9215600A001CE /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; - 9DF8056224F9215B00A001CE /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; - E1131B9CA4E134D6F01B8E27 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 9DE448232E0E7D6C008D9604 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; + 9DE448252E0E7D71008D9604 /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; }; + 9DE448272E0E7D76008D9604 /* AppTrackingTransparency.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppTrackingTransparency.framework; path = System/Library/Frameworks/AppTrackingTransparency.framework; sourceTree = SDKROOT; }; + 9DE448292E0E7D7A008D9604 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; + 9DE4482B2E0E7DB1008D9604 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = RunnerTests.xctest; path = "/Users/uerceg/GitHub/flutter_sdk_dev/example/ios/build/Debug-iphoneos/Runner.app/PlugIns/RunnerTests.xctest"; sourceTree = ""; }; + ACF65A8BFDE5CDDA65DDBFDC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E65025A74749A988F3B85BFF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 8F001A81A9C29896C07C7D24 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9D934E032612994B00BDDA0B /* AdServices.framework in Frameworks */, - 9DF8055F24F9215100A001CE /* AppTrackingTransparency.framework in Frameworks */, - 9DF8055D24F9214A00A001CE /* AdSupport.framework in Frameworks */, - 9DF8056124F9215600A001CE /* StoreKit.framework in Frameworks */, - B6025B3595AA1F9CF9F139AC /* Pods_Runner.framework in Frameworks */, + 88F4A3FD2DC44C7129665E88 /* Pods_Runner.framework in Frameworks */, + 9DE448282E0E7D76008D9604 /* AppTrackingTransparency.framework in Frameworks */, + 9DE4482A2E0E7D7A008D9604 /* StoreKit.framework in Frameworks */, + 9DE448242E0E7D6C008D9604 /* AdSupport.framework in Frameworks */, + 9DE448262E0E7D71008D9604 /* AdServices.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 19C31DF7D5B6DFB9032481D8 /* Pods */ = { + 4ECF1B705801FDADAE8C879B /* Frameworks */ = { isa = PBXGroup; children = ( - E1131B9CA4E134D6F01B8E27 /* Pods-Runner.debug.xcconfig */, - 5F31F7FF83E0ADA732B55E3B /* Pods-Runner.release.xcconfig */, - 33F9C572C86811AA0C86097B /* Pods-Runner.profile.xcconfig */, + 9DE448292E0E7D7A008D9604 /* StoreKit.framework */, + 9DE448272E0E7D76008D9604 /* AppTrackingTransparency.framework */, + 9DE448252E0E7D71008D9604 /* AdServices.framework */, + 9DE448232E0E7D6C008D9604 /* AdSupport.framework */, + DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -103,8 +123,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 9DF8055924F9214500A001CE /* Frameworks */, - 19C31DF7D5B6DFB9032481D8 /* Pods */, + EB003BCC806A08DFF19BEDB2 /* Pods */, + 4ECF1B705801FDADAE8C879B /* Frameworks */, ); sourceTree = ""; }; @@ -119,7 +139,6 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 9DD8A2442DF887F700A682DE /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -132,35 +151,53 @@ path = Runner; sourceTree = ""; }; - 9DF8055924F9214500A001CE /* Frameworks */ = { + EB003BCC806A08DFF19BEDB2 /* Pods */ = { isa = PBXGroup; children = ( - 9D934E022612994B00BDDA0B /* AdServices.framework */, - 9DF8056224F9215B00A001CE /* CoreTelephony.framework */, - 9DF8056024F9215600A001CE /* StoreKit.framework */, - 9DF8055E24F9215100A001CE /* AppTrackingTransparency.framework */, - 9DF8055C24F9214900A001CE /* AdSupport.framework */, - 9DF8055A24F9214500A001CE /* iAd.framework */, - 356E070BF8DE437AF960ADE2 /* Pods_Runner.framework */, + 303AB0B2394CD08FECD86B96 /* Pods-Runner.debug.xcconfig */, + 5D79E5F82EB64B9CD0A679F8 /* Pods-Runner.release.xcconfig */, + ACF65A8BFDE5CDDA65DDBFDC /* Pods-Runner.profile.xcconfig */, + 5B88428D631C6DEE5FAD69F8 /* Pods-RunnerTests.debug.xcconfig */, + E65025A74749A988F3B85BFF /* Pods-RunnerTests.release.xcconfig */, + 08D505F93980F9E52B00EC1A /* Pods-RunnerTests.profile.xcconfig */, ); - name = Frameworks; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + A38622A5BE32A9FB39BD74ED /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 8F001A81A9C29896C07C7D24 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 9DE4482B2E0E7DB1008D9604 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 432950737933D1855DAFE804 /* [CP] Check Pods Manifest.lock */, + D6E8E1968FC2B8AB21EC215F /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 8CC0374ACA4499C045686AA1 /* [CP] Embed Pods Frameworks */, + A0A004338F2FA5B43EBB0BB5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -177,9 +214,14 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; @@ -200,11 +242,19 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -235,63 +285,96 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 432950737933D1855DAFE804 /* [CP] Check Pods Manifest.lock */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + A0A004338F2FA5B43EBB0BB5 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 8CC0374ACA4499C045686AA1 /* [CP] Embed Pods Frameworks */ = { + A38622A5BE32A9FB39BD74ED /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + D6E8E1968FC2B8AB21EC215F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -303,6 +386,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -327,6 +418,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -346,7 +438,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -357,6 +448,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -368,7 +460,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -380,24 +472,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = QGUGW9AUMK; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -406,10 +488,61 @@ }; name = Profile; }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5B88428D631C6DEE5FAD69F8 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E65025A74749A988F3B85BFF /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 08D505F93980F9E52B00EC1A /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -429,7 +562,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -440,6 +572,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -466,6 +599,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -485,7 +619,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -496,6 +629,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -507,7 +641,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SUPPORTED_PLATFORMS = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; @@ -521,24 +655,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = QGUGW9AUMK; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -554,24 +678,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = QGUGW9AUMK; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.adjust.examples; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -583,6 +697,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ae420c9..e3773d4 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -38,10 +38,21 @@ + + + + Bool { GeneratedPluginRegistrant.register(with: self) - if let url = launchOptions?[.url] as? URL { - Adjust.processDeeplink(ADJDeeplink(deeplink: url)!) - } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - -override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { - NSLog("Scheme based deep link opened an app: %@", url.absoluteString) - // add your code below to handle deep link - // (e.g., open deep link content) - // url object contains the deep link - - // Call the below method to send deep link to Adjust backend - Adjust.processDeeplink(ADJDeeplink(deeplink: url)!) - return true - } - -override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { - if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { - NSLog("Universal link opened an app: %@", userActivity.webpageURL!.absoluteString) - // Pass deep link to Adjust in order to potentially reattribute user. - Adjust.processDeeplink(ADJDeeplink(deeplink: userActivity.webpageURL!)!) - } - return true - } } 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 28c6bf03016f6c994b70f38d1b7346e5831b531f..7353c41ecf9ca08017312dc233d9830079b50717 100644 GIT binary patch delta 279 zcmV+y0qFj;1g8R!8Gi!+006pI?LPnj0Blf9R7L;)|5U~J`u_j-{Qm)0oAmqtj@kOz z^8J|I`-|B6ht~R5kG+%I`zf~eztraM`u^bc{`dO)zUlmg)%x%C`E}6wSI77~z4s`y z^XT{f(eM4n?EUff`e@AgO~UxV*5*r_%Uhbj5N)LaQj!wdIe!-b004GLL_t&-)18pX z4udcZ1u-#g(~z+5JN*AY5?>Gw7hsN~k)CYt4dQDFxbs5*_&e@Hj)wtt(&JE<3Eq*D z;_gQLvqXoKv=I*gWqM9C(Tvu0>=?hTbOp9!6k6AF;>f6|S5%jGEE}TA9h)e`Yuiu8 d7)l?o1NFcJg%EAfM$P~L002ovPDHLkV1g^Dnv?(l delta 550 zcmV+>0@?ki0<;8>8Gi-<0051N9Sr~g00DDSM?wIu&K&6g00HhvL_t(I5v`QFOB_)Y z#?QI;j_a;jjf#Z$YJ7mH(xecJU?W)A`9CN~KrBV85C}GDQ=|;GDFPNjtWty!L{u=? zh>8yo%^GE+J9o~_IZFoiamQVQXP7%LzTbT3F@uf+9x&7cvVV%GdjTaC;zf>@mq<=3 z!c<%*UT)@yJ|0BK6~d4Jx-*KV`ZQ(@VyUPupum=XhInNG#Z_k-X|hK{B}~9IfiWx} zLD5QY6Vm)p0NrWymdkrHPN5Vgwd>5>4HI1=@PA+e^rq~CEj|n2X`??)0mUI*D{KBn zjv{V=y5X9|X@3grkpcXC6oou4ML~ezCc2EtnsQTB4tWNg?4bkf;hG7IMfhgNI(FV5 zGs4|*GyMTIY0$B=_*mso9+>eB z?J{?+FLkYu+4_Uk`r_>LHF~flZm0oBf#vr8%vJ>#p~!KNvqGG3)|f1T_)ydeh8$vDceZ>oNbH^|*hJ*t?Yc*1`WB&W>VYVEzu) zq#7;;VjO)t*nbgf(!`OXJBr45rP>>AQr$6c7slJWvbpNW@KTwna6d?PP>hvXCcp=4 zF;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f<+$JJpcdz delta 1274 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 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 4cde12118dda48d71e01fcb589a74d069c5d7cb5..4cd7b0099ca80c806f8fe495613e8d6c69460d76 100644 GIT binary patch delta 266 zcmV+l0rmcY2$}+r8Gi!+003c4mpuRg09{Z_R7L;)|5U~JDYo_jSDX9(|7FYh`2GLd z^Zv2r{H^2sT*&w!Y^SB+`<>qVZqE6)=lqo0`vF#&*75!I`TIh@_d&k*HoEtQyV-iD z%Xz2D9EQRbeYh5Nr~y=#0ZD;^+vz0$004MNL_t(2&&|%+4u6C&2tZM$Wf&dzefR%A z(^3-?6X>hnCz2Ba@RH&`m!pgy?n@#@AuLYB&}Q)FGY`?vcft0!vht0Z@M&ZeNCWXh75gzRTXR8EE3oN&6 Q00000NkvXXt^-0~g5kS`djJ3c delta 1014 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 Zxpx7IC@PekH?;r&002ovPDHLkV1i)CYaajr delta 1916 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 delta 1274 zcmV@pi1MCNO0zH7s z{8#}P0)7Ba8DqYf&QgSne>X__O83t$NZM4&R0{XJq|x}oAU?tcfC@|eNz$04T}34& z8DJf78R&>*Zz`k$q{`#gfGHnx7nlH^G{y`jfER)1<_fNi<9aM%_zrm1C`yPkKma(+ ztQ;y*CR2bbBYz>zG*SVsfpkGU(q>uHZf3iogk_%#9E|5SWeHrmAo>P;ejX7mwq#*} zW25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+X$F_KMdb6sRz!~7K zkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&IDi_4_D!s#MVXp|-XhH;H z#&@_;oApJVd}}5O@b=X_gJboD^-fM@6|#V@sA%X)Rlkd}3MLH0dGXGG&-HX|aD~|M zC)W#H7=H?AbtdaV#dGpubj_O^J-SlWpVNv-5(;wR%mvE9`Qaqo>03b&##eNNf=m#B z9@^lsd8tJ;BvI86kNV zc~0CY(7V{s+h%cWG|y=gt|q`z$l<(@qU=i?9q#uz`G?PgDMK!VMGidHZt*N+1L0ZI zFkH=mFtywc6rJ}C_?)=m)18V!ZQ`*-j(D`gCFK|nt#{bk*%%zuQ7o7kvJgA^=(^7b zzkm5GZ;jxRn{Wup8IOUx8D4uh&(=Ox-7$a;U><*5L^!% zxRlw)vAbh;sdlR||&e}8_8%)c2Fwy=F& zH|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}Jb#viX>Oi;kBKp1x_fc0#UIbIeSJ^EkWFox zijdim{ojmn@#7EC*aY;fC0W*WN+DmQtE06pNK3SfZ^#@2K`6RgEuU_KwJTQ>E?Yar zc_9e#I$F8%>kuy-JI6ocSsYvQGbsxUCx04(w1z-pMRz9`kH5smmF@WHEG?dcYkv){ zV?kn3XB$_3zr*h1Uow)(<5)w5;3Wh1jHI)`ZlXp&!yEV{Y_~@;?CLwq;4eeaGOe6( zEsSSbwSGD0-`dUUl014$1_O8Gi!+006nq0-pc?0H{z*R7L;)|5U~JDYo_jSDXF*|5nEMy6F5^ z$M}8I`uzU?*Yf=uXr;5|{0m;6_Wb|A>ik^D_|)+I$?g3CSDK^3+eX0mD!2CP`2NN0 z{dLg!a?km&%iyTt`yiax0acdp`~T(l{$a`ZF1YpsRg(cvjDG_-U$Er-fz#Bw>2W$eUI#iU z)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G!hkE!s;%oku3;IwG3U^2k zw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn_j%}l|2+O?a>_7qq7W zmx(qtA2nV^tZlLpy_#$U%ZNx5;$`0L&dZ!@e7rFXPGAOup%q`|03hpdtXsPP0000< KMNUMnLSTZ1N;Pr- delta 1891 zcmV-p2b}oI1m_Nr8Gi-<0052=@~r>>2QEoOK~#9!?VW3E6jc<*XLh$yKNt;)Mial3 z7z%<>zxaV5DhMs*(b6YIW1=KP6Jj(m21QYbiJ}su&;o5EN=$%gptMj6p|(7#AOTUJ zlt8fsX(iGq?ZQ50=XmbU+~w|cmz~|6$KBbz$-g^IcV>Hk`+q<8%-p?uMi3G-0B~!5 ze-yPCwFPw?HGmpMc~K)7BCq;C528+>zC*o^8h^XKC)IFgkv#xzm!ewK7j|kRa9dFo zC>MoDSR@P2#cWSU{i1oH5K2-Xb3jRz>|h7VOh0K` zhq^--L3H}A0r)nr z;Tr|-kPjB1s=ItpnS`oT%|U=a4oK-ZFIE^YBLH{u2#~@%%D^K)$`9*Tg(~9M-B+Zj z;~H?4LVsEt0eFtN4&>H(DZ@KpI6RhBKLL21CxC`J&m4Gc^9wwMZU#7SR1+KtuhSZM z+yLY}Vekzw6T_ApfEkuB_yU;e&a)L@rX~z70A_N+upOXN!qygmPDmKG0d%7CECcAI zgkd>ArzH$a0XjKsO$X@IgkcH5Y;m3`0G*yNOn(KK4GF_EfL4aB5i1j9o&Z{vFk~k> z&?@K2jQcJO%W!cddG(_DyfSoO55bUMHtbDF8DPkwF^~Ql#Eq4w15k{h%ML5Ar&pzi zl-D7v8kQXQ!&RRgKCW#5DZB$$6?mjWm50rRw*ukK>P-GkA|k69h{NARc>e}uLx+U4 z0DqE>7pa}9Fez+Vc-3jb`%i^uulglFoMzAVR|2%rf= zf#;74FXF^Ku_4+G&-4$KVy%YP>%2rxu2VG_cdm?XRjEhF&wPXJ># z_Q2+jGs=l~Fyx#MmGn+PZ0`@kBfGp|fO;Vov<$;z`(+sSZ7;Y=zXaF(8rb@CuQDV^ zq3i(2LfqO%AS!Ss>V%j7%>{6mtbYQrtQK5V4InPq0NZSaXv+f2U=&2}Z6OvkBfNHi z{LSaVJ!d5dC2K*ft_L^DRk;boQhOoVw!~Kt#0b2vd%!(&DF|~u1F@nG#LA5zR&7Fv z4GKgXooMSKb1g)6Obo-rgpuEP20T;W0Aa>55KC4gtQrKkAq-Hgs@FigV1GG8+rQ=z z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRet3L_uNyQ*c zE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=kyx=~RKa4{iT zm{_>_vSCm?$Ej=i6@=m%@PE9t1zZaoM}@2|h!#1K02~31S_I<0ZV=|K0}n!RRX6Ac zXmMf*5P-dLW}WPVsCKq)-x(0*txpZ2xrv3cxJ%l=7lpoNCyG< zK92ySAcmb-3m&}s@VwXv9(0#p<>B-5$bMxT;rk;OmENa6eM4D&LVo~01soUL39?R{ zyFLt3m|v?rCK7#KNu9E9Q4KV-pEUv^{rrClE&X&9I4-e7%pu_31#zGTOfC=ab%w20R*zBP+uT#l2{a~~~0wuG%6 zco*tVxK&e>%SJj*K!2tq*_h&ES5S9@TKb8WzpK;`&b9dNdxh4S)z%Q)o`aYWUh}9L z(`p!#WO5IxI|nf?yz{90R93Ed6@2qim*}Zjj$H&Esd`?JsFJUnDfiAgF_eYiWR3GC z>M9SHDylEWrA(%mfm~;u7OU9!Wz^!7Z%jZF zi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i0WYBP d*#0Ks^FNSabJA*5${_#%002ovPDHLkV1gB0Vle;! 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 a6d6b8609df07bf62e5100a53a01510388bd2b22..0ec303439225b78712f49115768196d8d76f6790 100644 GIT binary patch delta 850 zcmV-Y1Fih&6y64q8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f@rA97ytkO literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ 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 a6d6b8609df07bf62e5100a53a01510388bd2b22..0ec303439225b78712f49115768196d8d76f6790 100644 GIT binary patch delta 850 zcmV-Y1Fih&6y64q8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f@rA97ytkO literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ 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 75b2d164a5a98e212cca15ea7bf2ab5de5108680..e9f5fea27c705180eb716271f41b582e76dcbd90 100644 GIT binary patch delta 1668 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 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 c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..84ac32ae7d989f82d5e46a60405adcc8279e8001 100644 GIT binary patch delta 749 zcmVg;Ps8|O$@u8^{Z_{KM!@$5TAfS6_e#O{MZfpz`2O`0$7~@NRr(1{THzH08y3x{{PYM{eL;T_A9^tcF_4Sxb`8l z_9V3RD6;a(-0A^Pjsi!1?)d#Ap4Tk3^CP0(07;VpJ7@tgQ}z4)*zx@&yZwC9`DV-b z0ZobH_5IB4{KxD3;p_6%|f=bdFhu+F!zMZ2UFj;GUKX7tI;hv3{q~!*pMj75WP_c}> z6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FMs~w_u?Av_yNBmRxVYrpi(M% zFMP21g+hmocQp3ay*Su=qM6He)*HaaTg$E^sym`(t%s3A)x!M+vfjXUBEpK6X9%iU zU!u9jj3(-$dM~sJ%Liy#?|+!6IY#MTau#O6vVj`yh_7%Ni!?!VS+MPTO(_fG+1<#p zqu;A#i+_(N%CmVnYvb>#nA{>Q%3E`Ds7<~jZMywn@h2t>G-LrYy7?Dj{aZqhQd6tzX%(Trn+ z)HNF}%-F{rr=m*0{=a;s#YDL00000NkvXXu0mjfaGjYE delta 1884 zcmV-i2c!7<1>g>l8Gi-<0076AQ7Zrd2Pa8HK~#9!?VNjT6h$1z_m0EFf5bmb1dTDK zp;kdKV1h(V(8Sc1M<37!RE>znAk{x4#zX@eOeE1j3~!+nB5IL z<xS}u?#DBMB>w^b($1Z)`9G?eP95EKi& z$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD?Uu$P4(=PGA0ShFasNfcIHTL?9WjB9#(2xSLC z`0%$#9DW9F;B4mbU{BlaYx!SjF!QSeF~(msQRxwboh5B_O$BWOQja)GboJz$&!?mgB&3$ytsA zvns&b3Cl5Hx#%p%faR*Q906u&fbXy$maV`n?S>A)vJIH!F-vxCrY+rq5_JA(GcOgu7(Ky4X3ATR9z8*%k&<5qYeV&4Y`~}XYmK(j{)!g8d2UgHXIINM!Rvn zKtEq~Foe0s!U{kux~F6Y7Sp+2f|*Cc${S{@oh8D0=XhB8Ec-w9CflfL+te4ium2cU zoPTCj_m<3d#gjK=<*8R`HP^C$lOPM5d~UhKhRRmvv{LI za^|oavk1$QiEApSrP@~Jjbg`<*dW4TO@DPEEX$Tg$xh?Y>Qd}y@kaH~IT8!lLpS^J zR7(&wZSI6+>Eb)tX>9Z?GX#q$u z4I>7e#b7ojyJ1grOh!^}s8S#ubi^Jkd1?UK)3mp6rI^_zxRY zrx6_QmhoWoDR`fp4R7gu6@OBFGu7IDVR6~nJsB{^f5jHn<{WJ&&f^X?3f8TIk3#U& zu1*Q-e@;snJxNx8-PBnpI|uFTKN!+Lp;fPfZ+eqqU^Y1|#DJY~126?zOx-+d>%4*? z&o`TbrXSNXZW^!P0t2>@$6&aiBtUDh2wLXLD9&a(1J=k_FK|iGbAQ@x4Qmx}Ms+*; zze&q6bH(=wYuXHfz0H6<05!LkE4rl~v^!bj=^9d+vI5fN<;GP>*Pas=q2l9RxDkk` zPRk&EQI+t_0$Y%nKE)Ma)W?jaA@4Z{h zTk*7;;#lG?hvTN_On=Jaxp%bdE;mDq(q#dgdYF|-?wrMeI4h`$idZ6^VyXZVlaCd0 z;i)OYR3npf@9>00Gqn##Zb4HRurgaWFCzL9u6@J@sse>Z1XznxWvSy%Td32I3!#YN zXt9v0)RQtDDZRd?#WY?~KF7A0UcR{jt9 W+;fr}hV%pg0000&=UXv0SHh`R7L;)|5U~JDYo_jSDRDC`1<|-SjPDL z{{Q{{{{H{}09Kk-#rR9Y_viNgVafPO!S|ls`uzR=MZfp^{QU=8od8La1X`Tr_Wmff z_5e$ivgQ1@=KMy$_g9a+`TPAle6cOJ_Fc#L7qIpvwDkd1mw$fK`6IOUD75rX!}mad zv(fMTE4=(Nx%L54lL1hVF1YpqNrC`FddBPg#_Ietx%Lrkq5wX00X1L{S%Cm9QY*av z#_Rh5PKy9KYTWbvz3BX9%J>0Hi1+#X{rLA{m%$Kamk?i!03AC38#Yrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`?TG`AHia671e^vgmp!llK zp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?tc*y?iZ$PR7_ceEIapF3KB14K0Pog?7wtd+^xgUCa_GVmlD z<^nU>AU_Yn-JU?NFdu|wf^bTCNf-wSBYVZltDdvGBln-YrbeGvJ!|s{#`gjN@yAMb zM6cjFz0eFECCsc|_8hTa3*9-JQGehksdoVP^K4m?&wpA~+|b%{EP5D-+7h)6CE; z*{>BP=GRR3Ea}xyV*bqry{l^J=0#DaC4ej;1qs8_by?H6Tr@7hl>UKNZt)^B&yl;)&oqzLg zcfZxpE?3k%_iTOVywh%`XVN-E#COl+($9{v(pqSQcrz=)>G!!3HeNxbXGM@})1|9g zG4*@(OBaMvY0P0_TfMFPh fVHk#CZX3S=^^2mI>Ux-D00000NkvXXu0mjfzK(<8 literal 3294 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 cX)(b1KSk$MOV07*qoM6N<$f&{Qds= z{r_0T`1}6fwc-8!#-TGX}_?g)CZq4{k!uZ_g@DrQdoW0kI zu+W69&uN^)W`CK&06mMNcYMVF00dG=L_t(|+U?wHQxh>12H+Dm+1+fh+IF>G0SjJM zkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJTkdTm&kdTm&kdTm&kdP`e zsgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>VI$fQI%^ugM`#6By?GeadWcu z0gy9!D`m!H>Bd!JW(@avE8`|5XX(0PN}!8K>`dkavs;rHL+wy96QGNT=S@#7%xtlm zIW!++@*2zm-Py#Zr`DzqsLm!b{iskFNULSqE9A>SqHem>o31A%XL>S_5?=;V_i_y+ z(xxXhnt#r-l1Y8_*h`r?8Tr|)(RAiO)4jQR`13X0mx07C&p@KBP_2s``KEhv^|*8c z$$_T(v6^1Ig=#R}sE{vjA?ErGDZGUsyoJuWdJMc7Nb1^KF)-u<7q zPy$=;)0>vuWuK2hQhswLf!9yg`88u&eBbR8uhod?Nw09AXH}-#qOLLxeT2%C;R)QQ$Za#qp~cM&YVmS4i-*Fpd!cC zBXc?(4wcg>sHmXGd^VdE<5QX{Kyz$;$sCPl(_*-P2Iw?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF34$0Z;QO!J zOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUCUoZo%k(yku QW&i*H07*qoM6N<$g47z!?*IS* literal 3612 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 iW8 - CADisableMinimumFrameDurationOnPhone - CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + AdjustExample CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,33 +13,17 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - example + AdjustExample CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleSignature ???? - CFBundleURLTypes - - - CFBundleTypeRole - com.adjust.examples - CFBundleURLSchemes - - adjustexample - - - - CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS - NSUserTrackingUsageDescription - This identifier will be used to deliver personalized ads and improve app analytics. - UIApplicationSupportsIndirectInputEvents - UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -57,7 +41,11 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - + CADisableMinimumFrameDurationOnPhone + + NSUserTrackingUsageDescription + This is needed to access IDFA + UIApplicationSupportsIndirectInputEvents + diff --git a/example/ios/Runner/Runner.entitlements b/example/ios/Runner/Runner.entitlements deleted file mode 100644 index 5df5cf7..0000000 --- a/example/ios/Runner/Runner.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/example/lib/main.dart b/example/lib/main.dart index 73ceaa9..1b28c65 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,434 +1,466 @@ import 'package:adjust_sdk/adjust.dart'; import 'package:adjust_sdk/adjust_attribution.dart'; import 'package:adjust_sdk/adjust_config.dart'; +import 'package:adjust_sdk/adjust_event.dart'; import 'package:adjust_sdk/adjust_event_failure.dart'; import 'package:adjust_sdk/adjust_event_success.dart'; import 'package:adjust_sdk/adjust_session_failure.dart'; import 'package:adjust_sdk/adjust_session_success.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'util.dart'; - void main() { - runApp(MyApp()); + runApp(const AdjustExampleApp()); } -class MyApp extends StatelessWidget { - // This widget is the root of your application. +class AdjustExampleApp extends StatelessWidget { + const AdjustExampleApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'Adjust Example', theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. primarySwatch: Colors.blue, - // This makes the visual density adapt to the platform that you run - // the app on. For desktop platforms, the controls will be smaller and - // closer together (more dense) than on mobile platforms. visualDensity: VisualDensity.adaptivePlatformDensity, + scaffoldBackgroundColor: const Color(0xFFF5F5F5), ), - home: MyHomePage(title: 'Flutter Demo Home Page'), + home: const HomePage(), ); } } -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String? title; +class HomePage extends StatefulWidget { + const HomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _HomePageState(); } -class _MyHomePageState extends State with WidgetsBindingObserver { - bool _isSdkEnabled = true; +class _HomePageState extends State with WidgetsBindingObserver { + // Event tokens + static const String eventTokenSimple = 'g3mfiw'; + static const String eventTokenRevenue = 'a4fd35'; + static const String eventTokenCallback = '34vgg9'; + static const String eventTokenPartner = 'w788qs'; + + // Button text for SDK toggle + String _toggleButtonText = 'Toggle SDK'; @override - initState() { + void initState() { super.initState(); - WidgetsBinding.instance!.addObserver(this); - initPlatformState(); + WidgetsBinding.instance.addObserver(this); + _initializeAdjustSdk(); + _updateToggleButtonText(); } @override void dispose() { - WidgetsBinding.instance!.removeObserver(this); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - switch (state) { - case AppLifecycleState.inactive: - break; - case AppLifecycleState.resumed: - break; - case AppLifecycleState.paused: - break; - case AppLifecycleState.detached: - break; - } - } - - // Platform messages are asynchronous, so we initialize in an async method. - initPlatformState() async { - AdjustConfig config = - new AdjustConfig('2fm9gkqubvpc', AdjustEnvironment.sandbox); + Future _initializeAdjustSdk() async { + final config = AdjustConfig('2fm9gkqubvpc', AdjustEnvironment.sandbox); config.logLevel = AdjustLogLevel.verbose; - config.attributionCallback = (AdjustAttribution attributionChangedData) { + // Set up callbacks + config.attributionCallback = (AdjustAttribution attribution) { print('[Adjust]: Attribution changed!'); - - if (attributionChangedData.trackerToken != null) { - print( - '[Adjust]: Tracker token: ' + attributionChangedData.trackerToken!); + if (attribution.trackerToken != null) { + print('[Adjust]: Tracker token: ${attribution.trackerToken}'); + } + if (attribution.trackerName != null) { + print('[Adjust]: Tracker name: ${attribution.trackerName}'); } - if (attributionChangedData.trackerName != null) { - print('[Adjust]: Tracker name: ' + attributionChangedData.trackerName!); + if (attribution.campaign != null) { + print('[Adjust]: Campaign: ${attribution.campaign}'); } - if (attributionChangedData.campaign != null) { - print('[Adjust]: Campaign: ' + attributionChangedData.campaign!); + if (attribution.network != null) { + print('[Adjust]: Network: ${attribution.network}'); } - if (attributionChangedData.network != null) { - print('[Adjust]: Network: ' + attributionChangedData.network!); + if (attribution.creative != null) { + print('[Adjust]: Creative: ${attribution.creative}'); } - if (attributionChangedData.creative != null) { - print('[Adjust]: Creative: ' + attributionChangedData.creative!); + if (attribution.adgroup != null) { + print('[Adjust]: Adgroup: ${attribution.adgroup}'); } - if (attributionChangedData.adgroup != null) { - print('[Adjust]: Adgroup: ' + attributionChangedData.adgroup!); + if (attribution.clickLabel != null) { + print('[Adjust]: Click label: ${attribution.clickLabel}'); } - if (attributionChangedData.clickLabel != null) { - print('[Adjust]: Click label: ' + attributionChangedData.clickLabel!); + if (attribution.costType != null) { + print('[Adjust]: Cost type: ${attribution.costType}'); } - if (attributionChangedData.costType != null) { - print('[Adjust]: Cost type: ' + attributionChangedData.costType!); + if (attribution.costAmount != null) { + print('[Adjust]: Cost amount: ${attribution.costAmount}'); } - if (attributionChangedData.costAmount != null) { - print('[Adjust]: Cost amount: ' + - attributionChangedData.costAmount!.toString()); + if (attribution.costCurrency != null) { + print('[Adjust]: Cost currency: ${attribution.costCurrency}'); } - if (attributionChangedData.costCurrency != null) { - print( - '[Adjust]: Cost currency: ' + attributionChangedData.costCurrency!); + if (attribution.jsonResponse != null) { + print('[Adjust]: JSON response: ${attribution.jsonResponse}'); + } + if (attribution.fbInstallReferrer != null) { + print('[Adjust]: FB install referrer: ${attribution.fbInstallReferrer}'); } }; - config.sessionSuccessCallback = (AdjustSessionSuccess sessionSuccessData) { + config.sessionSuccessCallback = (AdjustSessionSuccess sessionSuccess) { print('[Adjust]: Session tracking success!'); - - if (sessionSuccessData.message != null) { - print('[Adjust]: Message: ' + sessionSuccessData.message!); + if (sessionSuccess.message != null) { + print('[Adjust]: Message: ${sessionSuccess.message}'); } - if (sessionSuccessData.timestamp != null) { - print('[Adjust]: Timestamp: ' + sessionSuccessData.timestamp!); + if (sessionSuccess.timestamp != null) { + print('[Adjust]: Timestamp: ${sessionSuccess.timestamp}'); } - if (sessionSuccessData.adid != null) { - print('[Adjust]: Adid: ' + sessionSuccessData.adid!); + if (sessionSuccess.adid != null) { + print('[Adjust]: Adid: ${sessionSuccess.adid}'); } - if (sessionSuccessData.jsonResponse != null) { - print('[Adjust]: JSON response: ' + sessionSuccessData.jsonResponse!); + if (sessionSuccess.jsonResponse != null) { + print('[Adjust]: JSON response: ${sessionSuccess.jsonResponse}'); } }; - config.sessionFailureCallback = (AdjustSessionFailure sessionFailureData) { + config.sessionFailureCallback = (AdjustSessionFailure sessionFailure) { print('[Adjust]: Session tracking failure!'); - - if (sessionFailureData.message != null) { - print('[Adjust]: Message: ' + sessionFailureData.message!); + if (sessionFailure.message != null) { + print('[Adjust]: Message: ${sessionFailure.message}'); } - if (sessionFailureData.timestamp != null) { - print('[Adjust]: Timestamp: ' + sessionFailureData.timestamp!); + if (sessionFailure.timestamp != null) { + print('[Adjust]: Timestamp: ${sessionFailure.timestamp}'); } - if (sessionFailureData.adid != null) { - print('[Adjust]: Adid: ' + sessionFailureData.adid!); + if (sessionFailure.adid != null) { + print('[Adjust]: Adid: ${sessionFailure.adid}'); } - if (sessionFailureData.willRetry != null) { - print( - '[Adjust]: Will retry: ' + sessionFailureData.willRetry.toString()); + if (sessionFailure.willRetry != null) { + print('[Adjust]: Will retry: ${sessionFailure.willRetry}'); } - if (sessionFailureData.jsonResponse != null) { - print('[Adjust]: JSON response: ' + sessionFailureData.jsonResponse!); + if (sessionFailure.jsonResponse != null) { + print('[Adjust]: JSON response: ${sessionFailure.jsonResponse}'); } }; - config.eventSuccessCallback = (AdjustEventSuccess eventSuccessData) { + config.eventSuccessCallback = (AdjustEventSuccess eventSuccess) { print('[Adjust]: Event tracking success!'); - - if (eventSuccessData.eventToken != null) { - print('[Adjust]: Event token: ' + eventSuccessData.eventToken!); + if (eventSuccess.eventToken != null) { + print('[Adjust]: Event token: ${eventSuccess.eventToken}'); } - if (eventSuccessData.message != null) { - print('[Adjust]: Message: ' + eventSuccessData.message!); + if (eventSuccess.message != null) { + print('[Adjust]: Message: ${eventSuccess.message}'); } - if (eventSuccessData.timestamp != null) { - print('[Adjust]: Timestamp: ' + eventSuccessData.timestamp!); + if (eventSuccess.timestamp != null) { + print('[Adjust]: Timestamp: ${eventSuccess.timestamp}'); } - if (eventSuccessData.adid != null) { - print('[Adjust]: Adid: ' + eventSuccessData.adid!); + if (eventSuccess.adid != null) { + print('[Adjust]: Adid: ${eventSuccess.adid}'); } - if (eventSuccessData.callbackId != null) { - print('[Adjust]: Callback ID: ' + eventSuccessData.callbackId!); + if (eventSuccess.callbackId != null) { + print('[Adjust]: Callback ID: ${eventSuccess.callbackId}'); } - if (eventSuccessData.jsonResponse != null) { - print('[Adjust]: JSON response: ' + eventSuccessData.jsonResponse!); + if (eventSuccess.jsonResponse != null) { + print('[Adjust]: JSON response: ${eventSuccess.jsonResponse}'); } }; - config.eventFailureCallback = (AdjustEventFailure eventFailureData) { + config.eventFailureCallback = (AdjustEventFailure eventFailure) { print('[Adjust]: Event tracking failure!'); - - if (eventFailureData.eventToken != null) { - print('[Adjust]: Event token: ' + eventFailureData.eventToken!); + if (eventFailure.eventToken != null) { + print('[Adjust]: Event token: ${eventFailure.eventToken}'); } - if (eventFailureData.message != null) { - print('[Adjust]: Message: ' + eventFailureData.message!); + if (eventFailure.message != null) { + print('[Adjust]: Message: ${eventFailure.message}'); } - if (eventFailureData.timestamp != null) { - print('[Adjust]: Timestamp: ' + eventFailureData.timestamp!); + if (eventFailure.timestamp != null) { + print('[Adjust]: Timestamp: ${eventFailure.timestamp}'); } - if (eventFailureData.adid != null) { - print('[Adjust]: Adid: ' + eventFailureData.adid!); + if (eventFailure.adid != null) { + print('[Adjust]: Adid: ${eventFailure.adid}'); } - if (eventFailureData.callbackId != null) { - print('[Adjust]: Callback ID: ' + eventFailureData.callbackId!); + if (eventFailure.callbackId != null) { + print('[Adjust]: Callback ID: ${eventFailure.callbackId}'); } - if (eventFailureData.willRetry != null) { - print('[Adjust]: Will retry: ' + eventFailureData.willRetry.toString()); + if (eventFailure.willRetry != null) { + print('[Adjust]: Will retry: ${eventFailure.willRetry}'); } - if (eventFailureData.jsonResponse != null) { - print('[Adjust]: JSON response: ' + eventFailureData.jsonResponse!); + if (eventFailure.jsonResponse != null) { + print('[Adjust]: JSON response: ${eventFailure.jsonResponse}'); } }; config.deferredDeeplinkCallback = (String? uri) { - print('[Adjust]: Received deferred deeplink: ' + uri!); + print('[Adjust]: Received deferred deeplink: $uri'); }; - config.skanUpdatedCallback = (Map skanUpdateData) { + config.skanUpdatedCallback = (Map skanData) { print('[Adjust]: Received SKAN update information!'); - if (skanUpdateData["conversion_value"] != null) { - print('[Adjust]: Conversion value: ' + skanUpdateData["conversion_value"]!); + if (skanData["conversion_value"] != null) { + print('[Adjust]: Conversion value: ${skanData["conversion_value"]}'); } - if (skanUpdateData["coarse_value"] != null) { - print('[Adjust]: Coarse value: ' + skanUpdateData["coarse_value"]!); + if (skanData["coarse_value"] != null) { + print('[Adjust]: Coarse value: ${skanData["coarse_value"]}'); } - if (skanUpdateData["lock_window"] != null) { - print('[Adjust]: Lock window: ' + skanUpdateData["lock_window"]!); + if (skanData["lock_window"] != null) { + print('[Adjust]: Lock window: ${skanData["lock_window"]}'); } - if (skanUpdateData["error"] != null) { - print('[Adjust]: Error: ' + skanUpdateData["error"]!); + if (skanData["error"] != null) { + print('[Adjust]: Error: ${skanData["error"]}'); } }; - // Add session callback parameters. + // Global parameters Adjust.addGlobalCallbackParameter('scp_foo_1', 'scp_bar'); Adjust.addGlobalCallbackParameter('scp_foo_2', 'scp_value'); - - // Add session Partner parameters. Adjust.addGlobalPartnerParameter('spp_foo_1', 'spp_bar'); Adjust.addGlobalPartnerParameter('spp_foo_2', 'spp_value'); - - // Remove session callback parameters. Adjust.removeGlobalCallbackParameter('scp_foo_1'); Adjust.removeGlobalPartnerParameter('spp_foo_1'); - - // Clear all session callback parameters. Adjust.removeGlobalCallbackParameters(); - - // Clear all session partner parameters. Adjust.removeGlobalPartnerParameters(); - // Ask for tracking consent. - Adjust.requestAppTrackingAuthorization().then((status) { - print('[Adjust]: Authorization status update!'); - final int statusInt = status.toInt(); - switch (statusInt) { - case 0: - print( - '[Adjust]: Authorization status update: ATTrackingManagerAuthorizationStatusNotDetermined'); - break; - case 1: - print( - '[Adjust]: Authorization status update: ATTrackingManagerAuthorizationStatusRestricted'); - break; - case 2: - print( - '[Adjust]: Authorization status update: ATTrackingManagerAuthorizationStatusDenied'); - break; - case 3: - print( - '[Adjust]: Authorization status update: ATTrackingManagerAuthorizationStatusAuthorized'); - break; - } - }); - - // start SDK + // Request tracking authorization + // Adjust.requestAppTrackingAuthorization().then((status) { + // print('[Adjust]: Authorization status update!'); + // switch (status.toInt()) { + // case 0: + // print('[Adjust]: ATTrackingManagerAuthorizationStatusNotDetermined'); + // break; + // case 1: + // print('[Adjust]: ATTrackingManagerAuthorizationStatusRestricted'); + // break; + // case 2: + // print('[Adjust]: ATTrackingManagerAuthorizationStatusDenied'); + // break; + // case 3: + // print('[Adjust]: ATTrackingManagerAuthorizationStatusAuthorized'); + // break; + // } + // }); + + // Initialize SDK Adjust.initSdk(config); } @override Widget build(BuildContext context) { - return new CustomScrollView( - shrinkWrap: true, - slivers: [ - new SliverPadding( - padding: const EdgeInsets.all(20.0), - sliver: new SliverList( - delegate: new SliverChildListDelegate( - [ - const Padding(padding: const EdgeInsets.all(7.0)), - - Util.buildCupertinoButton( - 'Is Enabled ?', () => _showIsSdkEnabled()), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Track simple event button. - Util.buildCupertinoButton('Track Simple Event', - () => Adjust.trackEvent(Util.buildSimpleEvent())), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Track revenue event button. - Util.buildCupertinoButton('Track Revenue Event', - () => Adjust.trackEvent(Util.buildRevenueEvent())), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Track callback event button. - Util.buildCupertinoButton('Track Callback Event', - () => Adjust.trackEvent(Util.buildCallbackEvent())), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Track partner event button. - Util.buildCupertinoButton('Track Partner Event', - () => Adjust.trackEvent(Util.buildPartnerEvent())), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Get Google Advertising Id. - Util.buildCupertinoButton( - 'Get Google AdId', - () => Adjust.getGoogleAdId().then((googleAdid) { - _showDialogMessage('Get Google Advertising Id', - 'Received Google Advertising Id: $googleAdid'); - })), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Get Adjust identifier. - Util.buildCupertinoButton( - 'Get Adjust identifier', - () => Adjust.getAdid().then((adid) { - _showDialogMessage('Adjust identifier', - 'Received Adjust identifier: $adid'); - })), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Get IDFA. - Util.buildCupertinoButton( - 'Get IDFA', - () => Adjust.getIdfa().then((idfa) { - _showDialogMessage('IDFA', 'Received IDFA: $idfa'); - })), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Get attribution. - Util.buildCupertinoButton( - 'Get attribution', - () => Adjust.getAttribution().then((attribution) { - _showDialogMessage('Attribution', - 'Received attribution: ${attribution.toString()}'); - })), - const Padding(padding: const EdgeInsets.all(7.0)), - - // Enable / disable SDK. - new Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - // Is SDK enabled switch. - new Text( - _isSdkEnabled ? 'Enabled' : 'Disabled', - style: _isSdkEnabled - ? new TextStyle(fontSize: 32.0, color: Colors.green) - : new TextStyle(fontSize: 32.0, color: Colors.red), - ), - new CupertinoSwitch( - value: _isSdkEnabled, - onChanged: (bool value) { - setState(() { - if(value == true){ - Adjust.enable(); - }else { - Adjust.disable(); - } - _isSdkEnabled = value; - print('Switch state = $_isSdkEnabled'); - }); - }, - ), - ], - ), - const Padding(padding: const EdgeInsets.all(7.0)), - - // end - ], - ), + return Scaffold( + appBar: AppBar( + title: const Text('Adjust Example'), + centerTitle: true, + backgroundColor: const Color(0xFF1B2951), // Adjust dark navy color + foregroundColor: Colors.white, + elevation: 0, + ), + body: Container( + width: double.infinity, + padding: const EdgeInsets.all(16.0), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFF1B2951), // Adjust dark navy + Color(0xFF2A3A5C), // Slightly lighter navy + ], + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildActionButton('Track Simple Event', _trackSimpleEvent), + const SizedBox(height: 12), + _buildActionButton('Track Revenue Event', _trackRevenueEvent), + const SizedBox(height: 12), + _buildActionButton('Track Callback Event', _trackCallbackEvent), + const SizedBox(height: 12), + _buildActionButton('Track Partner Event', _trackPartnerEvent), + const SizedBox(height: 12), + _buildActionButton('Get Google AdId', _getGoogleAdId), + const SizedBox(height: 12), + _buildActionButton('Get Adjust Identifier', _getAdjustIdentifier), + const SizedBox(height: 12), + _buildActionButton('Get IDFA', _getIdfa), + const SizedBox(height: 12), + _buildActionButton('Get Attribution', _getAttribution), + const SizedBox(height: 12), + _buildActionButton(_toggleButtonText, _toggleSdkState), + const SizedBox(height: 12), + _buildActionButton('Is Enabled?', _checkIfSdkEnabled), + ], + ), + ), + ); + } + + Widget _buildActionButton(String text, VoidCallback onPressed) { + return SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: const Color(0xFF1B2951), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), ), + elevation: 3, + shadowColor: Colors.black26, ), - ], + child: Text( + text, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), ); } - _showIsSdkEnabled() { + void _updateToggleButtonText() { try { Adjust.isEnabled().then((isEnabled) { - _isSdkEnabled = isEnabled; - _showDialogMessage('SDK Enabled?', 'Adjust is enabled = $isEnabled'); + setState(() { + _toggleButtonText = isEnabled ? 'Disable SDK' : 'Enable SDK'; + }); }); } on PlatformException { - _showDialogMessage( - 'SDK Enabled?', 'No such method found in plugin: isEnabled'); + // Keep default text if method not found } } - void _showDialogMessage(String title, String text, - [bool printToConsoleAlso = true]) { - if (printToConsoleAlso) { - print(text); + void _toggleSdkState() { + try { + Adjust.isEnabled().then((isEnabled) { + if (isEnabled) { + Adjust.disable(); + print('[Adjust]: SDK disabled'); + _showDialog('SDK State', 'Adjust SDK has been disabled'); + } else { + Adjust.enable(); + print('[Adjust]: SDK enabled'); + _showDialog('SDK State', 'Adjust SDK has been enabled'); + } + // Update button text after toggle + _updateToggleButtonText(); + }); + } on PlatformException { + _showDialog('Toggle SDK', 'No such method found in plugin: isEnabled'); } + } + void _checkIfSdkEnabled() { + try { + Adjust.isEnabled().then((isEnabled) { + _showDialog('SDK Enabled?', 'Adjust is enabled = $isEnabled'); + }); + } on PlatformException { + _showDialog('SDK Enabled?', 'No such method found in plugin: isEnabled'); + } + } + + void _trackSimpleEvent() { + final event = AdjustEvent(eventTokenSimple); + Adjust.trackEvent(event); + } + + void _trackRevenueEvent() { + final event = AdjustEvent(eventTokenRevenue); + event.setRevenue(100.0, 'EUR'); + event.transactionId = 'DummyTransactionId'; + Adjust.trackEvent(event); + } + + void _trackCallbackEvent() { + final event = AdjustEvent(eventTokenCallback); + event.addCallbackParameter('key1', 'value1'); + event.addCallbackParameter('key2', 'value2'); + Adjust.trackEvent(event); + } + + void _trackPartnerEvent() { + final event = AdjustEvent(eventTokenPartner); + event.addPartnerParameter('foo1', 'bar1'); + event.addPartnerParameter('foo2', 'bar2'); + Adjust.trackEvent(event); + } + + void _getGoogleAdId() { + Adjust.getGoogleAdId().then((googleAdId) { + _showDialog('Get Google Advertising Id', 'Received Google Advertising Id: $googleAdId'); + }); + } + + void _getAdjustIdentifier() { + Adjust.getAdid().then((adid) { + _showDialog('Adjust Identifier', 'Received Adjust identifier: $adid'); + }); + } + + void _getIdfa() { + Adjust.getIdfa().then((idfa) { + _showDialog('IDFA', 'Received IDFA: $idfa'); + }); + } + + void _getAttribution() { + Adjust.getAttribution().then((attribution) { + String attributionInfo = 'Attribution data:\n'; + if (attribution.trackerToken != null) { + attributionInfo += 'Tracker token: ${attribution.trackerToken}\n'; + } + if (attribution.trackerName != null) { + attributionInfo += 'Tracker name: ${attribution.trackerName}\n'; + } + if (attribution.campaign != null) { + attributionInfo += 'Campaign: ${attribution.campaign}\n'; + } + if (attribution.network != null) { + attributionInfo += 'Network: ${attribution.network}\n'; + } + if (attribution.creative != null) { + attributionInfo += 'Creative: ${attribution.creative}\n'; + } + if (attribution.adgroup != null) { + attributionInfo += 'Adgroup: ${attribution.adgroup}\n'; + } + if (attribution.clickLabel != null) { + attributionInfo += 'Click label: ${attribution.clickLabel}\n'; + } + if (attribution.costType != null) { + attributionInfo += 'Cost type: ${attribution.costType}\n'; + } + if (attribution.costAmount != null) { + attributionInfo += 'Cost amount: ${attribution.costAmount}\n'; + } + if (attribution.costCurrency != null) { + attributionInfo += 'Cost currency: ${attribution.costCurrency}\n'; + } + if (attribution.jsonResponse != null) { + attributionInfo += 'JSON response: ${attribution.jsonResponse}\n'; + } + if (attribution.fbInstallReferrer != null) { + attributionInfo += 'FB install referrer: ${attribution.fbInstallReferrer}\n'; + } + + _showDialog('Attribution', attributionInfo); + }); + } + + void _showDialog(String title, String message) { + print(message); showDialog( context: context, builder: (BuildContext context) { - return new CupertinoAlertDialog( + return AlertDialog( title: Text(title), - content: Text(text), + content: Text(message), actions: [ - new CupertinoDialogAction( + TextButton( + onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), - onPressed: () { - Navigator.pop(context, 'OK'); - }, - ) + ), ], ); }, diff --git a/example/lib/util.dart b/example/lib/util.dart deleted file mode 100644 index bdedf9a..0000000 --- a/example/lib/util.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:adjust_sdk/adjust_event.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class Util { - static const String EVENT_TOKEN_SIMPLE = 'g3mfiw'; - static const String EVENT_TOKEN_REVENUE = 'a4fd35'; - static const String EVENT_TOKEN_CALLBACK = '34vgg9'; - static const String EVENT_TOKEN_PARTNER = 'w788qs'; - - static Widget buildRaisedButton(String text, Function action) { - return new Align( - alignment: const Alignment(0.0, -0.2), - child: new Column( - mainAxisSize: MainAxisSize.min, - children: [ - new ButtonBar( - mainAxisSize: MainAxisSize.min, - children: [ - new ElevatedButton( - child: Text(text), - onPressed: () { - action(); - }, - ), - ], - ), - ], - ), - ); - } - - static Widget buildCupertinoButton(String text, Function action) { - return new CupertinoButton( - child: Text(text), - color: CupertinoColors.activeBlue, - padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 30.0), - onPressed: action as void Function()?, - ); - } - - static Widget buildRaisedButtonRow(String text, Function action) { - return new Align( - alignment: const Alignment(0.0, -0.2), - child: new Container( - child: new Row( - mainAxisSize: MainAxisSize.min, - children: [ - new ElevatedButton( - child: Text(text), - onPressed: () { - action(); - }, - ), - ], - ), - margin: new EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0), - padding: new EdgeInsets.all(1.0), - decoration: - new BoxDecoration(border: new Border.all(color: Colors.black)), - ), - ); - } - - static AdjustEvent buildSimpleEvent() { - return new AdjustEvent(EVENT_TOKEN_SIMPLE); - } - - static AdjustEvent buildRevenueEvent() { - AdjustEvent event = new AdjustEvent(EVENT_TOKEN_REVENUE); - event.setRevenue(100.0, 'EUR'); - event.transactionId = 'DummyTransactionId'; - return event; - } - - static AdjustEvent buildCallbackEvent() { - AdjustEvent event = new AdjustEvent(EVENT_TOKEN_CALLBACK); - event.addCallbackParameter('key1', 'value1'); - event.addCallbackParameter('key2', 'value2'); - return event; - } - - static AdjustEvent buildPartnerEvent() { - AdjustEvent event = new AdjustEvent(EVENT_TOKEN_PARTNER); - event.addPartnerParameter('foo1', 'bar1'); - event.addPartnerParameter('foo2', 'bar2'); - return event; - } - - static void showMessage( - BuildContext context, String dialogText, String message) { - showDialog( - context: context, - builder: (_) => new AlertDialog( - title: new Text(dialogText), - content: new Text(message), - )); - } - - static void showDemoDialog( - {GlobalKey? scaffoldKey, - required BuildContext context, - Widget? child}) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) => child!, - ).then((T? value) { - // The value passed to Navigator.pop() or null. - if (scaffoldKey != null && value != null) { - ScaffoldMessenger.of(context).showSnackBar( - new SnackBar( - content: new Text('You selected: $value'), - ), - ); - } - }); - } -} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e73f09c..ce8d8dc 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,24 +1,10 @@ -name: example -description: Adjust Flutter Appliaction - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +name: AdjustExample +description: "Adjust Flutter Example App" +publish_to: 'none' +version: 0.0.1 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ^3.9.0-277.0.dev dependencies: flutter: @@ -26,22 +12,10 @@ dependencies: adjust_sdk: path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 - dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^6.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 747db1d..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From bb89a5aaddf8a8d035e80e958cc3a60d916dbd94 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 13:25:38 +0200 Subject: [PATCH 04/17] refac: restyle test app --- test/app/lib/main.dart | 88 +++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/test/app/lib/main.dart b/test/app/lib/main.dart index 0fcf2fa..bf67dbd 100644 --- a/test/app/lib/main.dart +++ b/test/app/lib/main.dart @@ -26,13 +26,13 @@ class _MyAppState extends State { super.initState(); if (Platform.isAndroid) { - String _address = '192.168.8.78'; + String _address = '192.168.86.237'; String _protocol = 'https'; String _port = '8443'; _overwriteUrl = _protocol + '://' + _address + ':' + _port; _controlUrl = 'ws://' + _address + ':1987'; } else { - String _address = '192.168.8.78'; + String _address = '192.168.86.237'; String _protocol = 'http'; String _port = '8080'; _overwriteUrl = _protocol + '://' + _address + ':' + _port; @@ -56,36 +56,72 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { - return new MaterialApp( - home: new Scaffold( - appBar: new AppBar( - title: const Text('Adjust Flutter SDK Test App'), + return MaterialApp( + title: 'Adjust Test App', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: Scaffold( + appBar: AppBar( + title: const Text('Adjust Test App'), + centerTitle: true, + backgroundColor: const Color(0xFF1B2951), // Adjust dark navy color + foregroundColor: Colors.white, + elevation: 0, ), - body: new CustomScrollView(shrinkWrap: true, slivers: [ - new SliverPadding( + body: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFF1B2951), // Adjust dark navy + Color(0xFF2A3A5C), // Slightly lighter navy + ], + ), + ), + child: Center( + child: Padding( padding: const EdgeInsets.all(20.0), - sliver: new SliverList( - delegate: new SliverChildListDelegate([ - new Text('Running'), - buildCupertinoButton( - 'Start Test Session', - () => Adjust.getSdkVersion().then((sdkVersion) { - // TestLib.addTestDirectory('event-callbacks'); - // TestLib.addTest('Test_Parameters_FeatureFlags_present'); - TestLib.startTestSession(sdkVersion); - })) - ]))) - ]), + child: _buildActionButton( + 'Start Test Session', + () => Adjust.getSdkVersion().then((sdkVersion) { + TestLib.startTestSession(sdkVersion); + }), + ), + ), + ), + ), ), ); } - static Widget buildCupertinoButton(String text, Function action) { - return new CupertinoButton( - child: Text(text), - color: CupertinoColors.activeBlue, - padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 30.0), - onPressed: action as void Function()?, + Widget _buildActionButton(String text, VoidCallback onPressed) { + return SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: const Color(0xFF1B2951), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), + ), + elevation: 3, + shadowColor: Colors.black26, + ), + child: Text( + text, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), ); } } From ee0353b30d360d1919613c1a0ea448c4596a48e4 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 15:16:49 +0200 Subject: [PATCH 05/17] refac: restructure test app --- test/android/libs/adjust-test-options.jar | Bin 5686 -> 5190 bytes test/app/android/app/build.gradle | 81 ------------------ test/app/android/app/build.gradle.kts | 46 ++++++++++ test/app/android/build.gradle | 31 ------- test/app/android/build.gradle.kts | 24 ++++++ test/app/android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- test/app/android/settings.gradle | 35 -------- test/app/android/settings.gradle.kts | 26 ++++++ test/app/ios/Podfile | 10 +-- test/app/lib/command.dart | 5 +- test/app/lib/command_executor.dart | 6 +- test/app/lib/main.dart | 7 +- test/app/pubspec.yaml | 64 ++------------ 14 files changed, 112 insertions(+), 229 deletions(-) delete mode 100644 test/app/android/app/build.gradle create mode 100644 test/app/android/app/build.gradle.kts delete mode 100644 test/app/android/build.gradle create mode 100644 test/app/android/build.gradle.kts delete mode 100644 test/app/android/settings.gradle create mode 100644 test/app/android/settings.gradle.kts diff --git a/test/android/libs/adjust-test-options.jar b/test/android/libs/adjust-test-options.jar index e290b144044d62c8e0dd6a3a328a6f90b113e9d3..2e84644ee5db3aa3210dcca9282494ff4a7e91e2 100644 GIT binary patch delta 2995 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 Kv*e5ZjrtGcGJQM% delta 3400 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 - localProperties.load(reader) - } -} - -// def flutterRoot = localProperties.getProperty('flutter.sdk') -// if (flutterRoot == null) { -// throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -// } - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -// apply plugin: 'com.android.application' -// apply plugin: 'kotlin-android' -// apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - namespace 'com.adjust.app' - compileSdkVersion 35 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.adjust.app" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 35 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_18 - targetCompatibility JavaVersion.VERSION_18 - } - - kotlinOptions { - jvmTarget = "18" - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } - lint { - disable 'InvalidPackage' - } -} - -flutter { - source '../..' -} - -dependencies { - implementation 'com.google.android.gms:play-services-analytics:18.0.1' - implementation 'com.android.installreferrer:installreferrer:2.2' - implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.6.2' -} diff --git a/test/app/android/app/build.gradle.kts b/test/app/android/app/build.gradle.kts new file mode 100644 index 0000000..d858c29 --- /dev/null +++ b/test/app/android/app/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.adjust.app" + compileSdk = flutter.compileSdkVersion + + defaultConfig { + applicationId = "com.adjust.app" + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_18 + targetCompatibility = JavaVersion.VERSION_18 + } + + kotlinOptions { + jvmTarget = "18" + } + + buildTypes { + release { + signingConfig = signingConfigs.getByName("debug") + } + } + lint { + disable.add("InvalidPackage") + } +} + +flutter { + source = "../.." +} + +dependencies { + implementation("com.google.android.gms:play-services-analytics:18.0.1") + implementation("com.android.installreferrer:installreferrer:2.2") + implementation("org.slf4j:slf4j-simple:2.0.9") +} \ No newline at end of file diff --git a/test/app/android/build.gradle b/test/app/android/build.gradle deleted file mode 100644 index aed4dbe..0000000 --- a/test/app/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -// buildscript { -// ext.kotlin_version = '1.6.10' -// repositories { -// google() -// mavenCentral() -// } - -// dependencies { -// classpath 'com.android.tools.build:gradle:7.1.1' -// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" -// } -// } - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/test/app/android/build.gradle.kts b/test/app/android/build.gradle.kts new file mode 100644 index 0000000..ad4aba9 --- /dev/null +++ b/test/app/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} \ No newline at end of file diff --git a/test/app/android/gradle.properties b/test/app/android/gradle.properties index 94adc3a..f018a61 100644 --- a/test/app/android/gradle.properties +++ b/test/app/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/test/app/android/gradle/wrapper/gradle-wrapper.properties b/test/app/android/gradle/wrapper/gradle-wrapper.properties index df97d72..efdcc4a 100644 --- a/test/app/android/gradle/wrapper/gradle-wrapper.properties +++ b/test/app/android/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip -networkTimeout=10000 -validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip diff --git a/test/app/android/settings.gradle b/test/app/android/settings.gradle deleted file mode 100644 index 0af5039..0000000 --- a/test/app/android/settings.gradle +++ /dev/null @@ -1,35 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.2.1" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false -} - -include ':app' - -// def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -// def properties = new Properties() - -// assert localPropertiesFile.exists() -// localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -// def flutterSdkPath = properties.getProperty("flutter.sdk") -// assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -// apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/test/app/android/settings.gradle.kts b/test/app/android/settings.gradle.kts new file mode 100644 index 0000000..16f2310 --- /dev/null +++ b/test/app/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.9.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") \ No newline at end of file diff --git a/test/app/ios/Podfile b/test/app/ios/Podfile index dc9af58..17adeb1 100644 --- a/test/app/ios/Podfile +++ b/test/app/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '13.0' +# platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -29,20 +29,12 @@ 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| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |config| - config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' - config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'i386 arm64' - end end end diff --git a/test/app/lib/command.dart b/test/app/lib/command.dart index 93d255a..855dbda 100644 --- a/test/app/lib/command.dart +++ b/test/app/lib/command.dart @@ -29,8 +29,7 @@ class Command { } } catch (e) { print( - '[Command]: Error! Failed to map Command from incoming data. Details: ' + - e.toString()); + '[Command]: Error! Failed to map Command from incoming data. Details: ${e.toString()}'); } } @@ -39,7 +38,7 @@ class Command { String? getFirstParameterValue(String parameterKey) { List? parameterValues = _parameters![parameterKey]; - if (parameterValues == null || parameterValues.length == 0) { + if (parameterValues == null || parameterValues.isEmpty) { return null; } return parameterValues.first; diff --git a/test/app/lib/command_executor.dart b/test/app/lib/command_executor.dart index b710e5a..4ff54d7 100644 --- a/test/app/lib/command_executor.dart +++ b/test/app/lib/command_executor.dart @@ -41,8 +41,8 @@ class CommandExecutor { String? _overwriteUrl; String? _extraPath; late Command _command; - Map _savedEvents = new Map(); - Map _savedConfigs = new Map(); + Map _savedEvents = {}; + Map _savedConfigs = {}; CommandExecutor(String? overwriteUrl) { _baseUrl = overwriteUrl; @@ -1022,7 +1022,7 @@ class CommandExecutor { void _attributionGetter() { Adjust.getAttribution().then((attribution){ if(attribution != null) { - Map fields = new Map(); + Map fields = {}; fields["tracker_token"] = attribution.trackerToken; fields["tracker_name"] = attribution.trackerName; fields["network"] = attribution.network; diff --git a/test/app/lib/main.dart b/test/app/lib/main.dart index bf67dbd..c0455c8 100644 --- a/test/app/lib/main.dart +++ b/test/app/lib/main.dart @@ -13,7 +13,7 @@ void main() { class MyApp extends StatefulWidget { @override - _MyAppState createState() => new _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -40,13 +40,12 @@ class _MyAppState extends State { } // Initialise command executor. - _commandExecutor = - new CommandExecutor(_overwriteUrl); + _commandExecutor = CommandExecutor(_overwriteUrl); // Initialise test library. TestLib.setExecuteCommandHalder((final dynamic callArgs) { print('[AdjustTestApp]: executeCommandHandler pinged in test app!'); - Command command = new Command(callArgs); + Command command = Command(callArgs); print( '[AdjustTestApp]: Executing command ${command.className}.${command.methodName}'); _commandExecutor.executeCommand(command); diff --git a/test/app/pubspec.yaml b/test/app/pubspec.yaml index ba8adce..9a7954f 100644 --- a/test/app/pubspec.yaml +++ b/test/app/pubspec.yaml @@ -1,24 +1,10 @@ name: test_app -description: Adjust Flutter Test Application - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +description: "Adjust Flutter Test App" +publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ^3.9.0-277.0.dev dependencies: flutter: @@ -29,52 +15,12 @@ dependencies: path: ../ - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 + dev_dependencies: flutter_test: sdk: flutter + flutter_lints: ^6.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages From cf1076c54e52459bba18515f96f83501409e50fb Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 15:39:45 +0200 Subject: [PATCH 06/17] refac: continue test app refac spree --- test/app/README.md | 40 ++- test/app/android/app/build.gradle.kts | 30 +- .../adjust/{app => examples}/MainActivity.kt | 4 +- test/app/lib/command.dart | 23 +- test/app/lib/command_executor.dart | 68 ++-- test/app/lib/main.dart | 300 ++++++++++++++---- test/app/pubspec.yaml | 3 - test/app/test/widget_test.dart | 29 -- 8 files changed, 343 insertions(+), 154 deletions(-) rename test/app/android/app/src/main/kotlin/com/adjust/{app => examples}/MainActivity.kt (75%) delete mode 100644 test/app/test/widget_test.dart diff --git a/test/app/README.md b/test/app/README.md index 77323b2..81fd42e 100644 --- a/test/app/README.md +++ b/test/app/README.md @@ -1,16 +1,38 @@ -# app +# Adjust Flutter Test App -Adjust Flutter Test Application +A comprehensive test application for the Adjust Flutter SDK, designed to validate SDK functionality through automated testing. + +## Features + +- **Automated Test Execution**: Connects to test framework for automated SDK testing +- **Modern UI**: Clean, modern interface following Adjust design guidelines +- **Cross-Platform**: Supports both Android and iOS testing scenarios +- **Real-time Command Processing**: Executes test commands in real-time from test server ## Getting Started -This project is a starting point for a Flutter application. +1. **Prerequisites**: Make sure Flutter is installed and configured +2. **Install Dependencies**: Run `flutter pub get` in this directory +3. **Configure Test Server**: Update IP addresses in `main.dart` if needed +4. **Run the App**: Use `flutter run` to start the test application + +## Test Configuration + +The app connects to a test server for automated command execution: +- **Android**: Uses HTTPS connection on port 8443 +- **iOS**: Uses HTTP connection on port 8080 +- **WebSocket**: Control connection on port 1987 + +## Architecture -A few resources to get you started if this is your first Flutter project: +- `main.dart`: Main app entry point with modern UI +- `command.dart`: Command parsing and representation +- `command_executor.dart`: SDK method execution engine +- Test framework integration via `test_lib` package -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +## Usage -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +1. Launch the app +2. Tap "Start Test Session" to begin automated testing +3. The app will connect to the test server and execute commands automatically +4. Monitor console output for test execution details diff --git a/test/app/android/app/build.gradle.kts b/test/app/android/app/build.gradle.kts index d858c29..c0201c5 100644 --- a/test/app/android/app/build.gradle.kts +++ b/test/app/android/app/build.gradle.kts @@ -1,35 +1,38 @@ plugins { id("com.android.application") id("kotlin-android") + // the Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins id("dev.flutter.flutter-gradle-plugin") } android { - namespace = "com.adjust.app" + namespace = "com.adjust.examples" compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } defaultConfig { - applicationId = "com.adjust.app" + applicationId = "com.adjust.examples" minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_18 - targetCompatibility = JavaVersion.VERSION_18 - } - - kotlinOptions { - jvmTarget = "18" - } - buildTypes { release { signingConfig = signingConfigs.getByName("debug") } } + lint { disable.add("InvalidPackage") } @@ -40,7 +43,12 @@ flutter { } dependencies { + // google Play Services for analytics implementation("com.google.android.gms:play-services-analytics:18.0.1") + + // install referrer for attribution implementation("com.android.installreferrer:installreferrer:2.2") + + // modern logging framework implementation("org.slf4j:slf4j-simple:2.0.9") } \ No newline at end of file diff --git a/test/app/android/app/src/main/kotlin/com/adjust/app/MainActivity.kt b/test/app/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt similarity index 75% rename from test/app/android/app/src/main/kotlin/com/adjust/app/MainActivity.kt rename to test/app/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt index ab65b3c..615b428 100644 --- a/test/app/android/app/src/main/kotlin/com/adjust/app/MainActivity.kt +++ b/test/app/android/app/src/main/kotlin/com/adjust/examples/MainActivity.kt @@ -1,6 +1,6 @@ -package com.adjust.app +package com.adjust.examples import io.flutter.embedding.android.FlutterActivity class MainActivity: FlutterActivity() { -} +} \ No newline at end of file diff --git a/test/app/lib/command.dart b/test/app/lib/command.dart index 855dbda..b288b38 100644 --- a/test/app/lib/command.dart +++ b/test/app/lib/command.dart @@ -9,49 +9,62 @@ import 'dart:convert'; import 'dart:io'; +/// represents a test command received from the test library +/// contains class name, method name, and parameters for execution class Command { String? _className; String? _methodName; String? _jsonParameters; Map? _parameters; + /// creates a Command from a dynamic map received from the test framework Command(dynamic map) { try { _className = map['className']; _methodName = map['methodName']; if (Platform.isAndroid) { + // android sends parameters as JSON string _jsonParameters = map['jsonParameters']; _parameters = json.decode(_jsonParameters!); } else { + // iOS sends parameters as map directly _parameters = map['jsonParameters']; _jsonParameters = json.encode(_parameters); } } catch (e) { - print( - '[Command]: Error! Failed to map Command from incoming data. Details: ${e.toString()}'); + print('[Command]: Failed to parse command from incoming data. Error: $e'); } } + /// the class name to execute the method on String? get className => _className; + + /// the method name to execute String? get methodName => _methodName; + /// gets the first value for a given parameter key + /// returns null if parameter doesn't exist or has no values String? getFirstParameterValue(String parameterKey) { - List? parameterValues = _parameters![parameterKey]; + final List? parameterValues = _parameters![parameterKey]; if (parameterValues == null || parameterValues.isEmpty) { return null; } return parameterValues.first; } - List? getParamteters(String parameterKey) { + /// gets all parameter values for a given key + /// fixed typo: was "getParamteters", now "getParameters" + List? getParameters(String parameterKey) { return _parameters![parameterKey]; } + /// checks if the command contains a specific parameter bool containsParameter(String parameterKey) { - return _parameters![parameterKey] != null; + return _parameters?[parameterKey] != null; } + /// returns a string representation of the command for debugging @override String toString() { return 'Command[className: $_className, methodName: $_methodName, jsonParameters: $_jsonParameters]'; diff --git a/test/app/lib/command_executor.dart b/test/app/lib/command_executor.dart index 4ff54d7..9062718 100644 --- a/test/app/lib/command_executor.dart +++ b/test/app/lib/command_executor.dart @@ -29,6 +29,8 @@ import 'package:adjust_sdk/adjust_store_info.dart'; import 'package:test_app/command.dart'; import 'package:test_lib/test_lib.dart'; +/// executes test commands received from the test framework +/// handles all Adjust SDK method calls and configurations class CommandExecutor { String? _baseUrl; String? _basePath; @@ -41,9 +43,10 @@ class CommandExecutor { String? _overwriteUrl; String? _extraPath; late Command _command; - Map _savedEvents = {}; - Map _savedConfigs = {}; + final Map _savedEvents = {}; + final Map _savedConfigs = {}; + /// creates a new CommandExecutor with the specified base URL CommandExecutor(String? overwriteUrl) { _baseUrl = overwriteUrl; _gdprUrl = overwriteUrl; @@ -52,6 +55,7 @@ class CommandExecutor { _overwriteUrl = overwriteUrl; } + /// executes the given command by dispatching to the appropriate method void executeCommand(Command command) { _command = command; switch (command.methodName) { @@ -151,6 +155,7 @@ class CommandExecutor { } } + /// configure test options for the SDK void _testOptions() { final dynamic testOptions = {}; testOptions['baseUrl'] = _overwriteUrl; @@ -158,6 +163,7 @@ class CommandExecutor { testOptions['subscriptionUrl'] = _overwriteUrl; testOptions['purchaseVerificationUrl'] = _overwriteUrl; testOptions['urlOverwrite'] = _overwriteUrl; + if (_command.containsParameter('basePath')) { _basePath = _command.getFirstParameterValue('basePath'); _gdprPath = _command.getFirstParameterValue('basePath'); @@ -165,54 +171,66 @@ class CommandExecutor { _purchaseVerificationPath = _command.getFirstParameterValue('basePath'); _extraPath = _command.getFirstParameterValue('basePath'); } + if (_command.containsParameter('timerInterval')) { testOptions['timerIntervalInMilliseconds'] = _command.getFirstParameterValue('timerInterval'); } + if (_command.containsParameter('timerStart')) { testOptions['timerStartInMilliseconds'] = _command.getFirstParameterValue('timerStart'); } + if (_command.containsParameter('sessionInterval')) { testOptions['sessionIntervalInMilliseconds'] = _command.getFirstParameterValue('sessionInterval'); } + if (_command.containsParameter('subsessionInterval')) { testOptions['subsessionIntervalInMilliseconds'] = _command.getFirstParameterValue('subsessionInterval'); } + if (_command.containsParameter('tryInstallReferrer')) { testOptions['tryInstallReferrer'] = _command.getFirstParameterValue('tryInstallReferrer'); } + if (_command.containsParameter('noBackoffWait')) { testOptions['noBackoffWait'] = _command.getFirstParameterValue('noBackoffWait'); } + if (_command.containsParameter("doNotIgnoreSystemLifecycleBootstrap")) { - String? doNotIgnoreSystemLifecycleBootstrapString = - _command.getFirstParameterValue("doNotIgnoreSystemLifecycleBootstrap"); - bool doNotIgnoreSystemLifecycleBootstrap = (doNotIgnoreSystemLifecycleBootstrapString == 'true'); + final String? doNotIgnoreSystemLifecycleBootstrapString = + _command.getFirstParameterValue("doNotIgnoreSystemLifecycleBootstrap"); + final bool doNotIgnoreSystemLifecycleBootstrap = + (doNotIgnoreSystemLifecycleBootstrapString == 'true'); if (doNotIgnoreSystemLifecycleBootstrap) { testOptions['ignoreSystemLifecycleBootstrap'] = false; } } + if (_command.containsParameter('adServicesFrameworkEnabled')) { testOptions['adServicesFrameworkEnabled'] = _command.getFirstParameterValue('adServicesFrameworkEnabled'); } + if (_command.containsParameter('attStatus')) { testOptions['attStatus'] = _command.getFirstParameterValue('attStatus'); } + if (_command.containsParameter('idfa')) { testOptions['idfa'] = _command.getFirstParameterValue('idfa'); } + bool useTestConnectionOptions = false; if (_command.containsParameter('teardown')) { - List teardownOptions = _command.getParamteters('teardown')!; - for (String teardownOption in teardownOptions) { + final List teardownOptions = _command.getParameters('teardown')!; + for (final String teardownOption in teardownOptions) { if (teardownOption == 'resetSdk') { testOptions['teardown'] = 'true'; testOptions['basePath'] = _extraPath; @@ -220,7 +238,7 @@ class CommandExecutor { testOptions['subscriptionPath'] = _extraPath; testOptions['purchaseVerificationPath'] = _extraPath; testOptions['extraPath'] = _extraPath; - // Android specific + // android specific testOptions['useTestConnectionOptions'] = 'true'; testOptions['tryInstallReferrer'] = 'false'; useTestConnectionOptions = true; @@ -242,7 +260,7 @@ class CommandExecutor { testOptions['gdprPath'] = null; testOptions['subscriptionPath'] = null; testOptions['extraPath'] = null; - // Android specific. + // android specific testOptions['useTestConnectionOptions'] = 'false'; useTestConnectionOptions = false; } @@ -578,14 +596,14 @@ class CommandExecutor { } if (_command.containsParameter('revenue')) { - List revenueParams = _command.getParamteters('revenue')!; + List revenueParams = _command.getParameters('revenue')!; // TODO: find better way to filter null values for Flutter platform if (revenueParams[0] != null && revenueParams[1] != null) { adjustEvent!.setRevenue(num.parse(revenueParams[1]), revenueParams[0]); } } if (_command.containsParameter('callbackParams')) { - List callbackParams = _command.getParamteters('callbackParams')!; + List callbackParams = _command.getParameters('callbackParams')!; for (int i = 0; i < callbackParams.length; i = i + 2) { if (callbackParams[i] != null && callbackParams[i + 1] != null) { String key = callbackParams[i]; @@ -595,7 +613,7 @@ class CommandExecutor { } } if (_command.containsParameter('partnerParams')) { - List partnerParams = _command.getParamteters('partnerParams')!; + List partnerParams = _command.getParameters('partnerParams')!; for (int i = 0; i < partnerParams.length; i = i + 2) { if (partnerParams[i] != null && partnerParams[i + 1] != null) { String key = partnerParams[i]; @@ -686,7 +704,7 @@ class CommandExecutor { return; } - List keyValuePairs = _command.getParamteters('KeyValue')!; + List keyValuePairs = _command.getParameters('KeyValue')!; for (int i = 0; i < keyValuePairs.length; i = i + 2) { String key = keyValuePairs[i]; String value = keyValuePairs[i + 1]; @@ -699,7 +717,7 @@ class CommandExecutor { return; } - List keyValuePairs = _command.getParamteters('KeyValue')!; + List keyValuePairs = _command.getParameters('KeyValue')!; for (int i = 0; i < keyValuePairs.length; i = i + 2) { String key = keyValuePairs[i]; String value = keyValuePairs[i + 1]; @@ -712,7 +730,7 @@ class CommandExecutor { return; } - List keys = _command.getParamteters('key')!; + List keys = _command.getParameters('key')!; for (int i = 0; i < keys.length; i = i + 1) { String key = keys[i]; Adjust.removeGlobalCallbackParameter(key); @@ -724,7 +742,7 @@ class CommandExecutor { return; } - List keys = _command.getParamteters('key')!; + List keys = _command.getParameters('key')!; for (int i = 0; i < keys.length; i = i + 1) { String key = keys[i]; Adjust.removeGlobalPartnerParameter(key); @@ -756,7 +774,7 @@ class CommandExecutor { if (_command.containsParameter('callbackParams')) { List callbackParams = - _command.getParamteters('callbackParams')!; + _command.getParameters('callbackParams')!; for (int i = 0; i < callbackParams.length; i = i + 2) { if (callbackParams[i] != null && callbackParams[i + 1] != null) { String key = callbackParams[i]; @@ -766,7 +784,7 @@ class CommandExecutor { } } if (_command.containsParameter('partnerParams')) { - List partnerParams = _command.getParamteters('partnerParams')!; + List partnerParams = _command.getParameters('partnerParams')!; for (int i = 0; i < partnerParams.length; i = i + 2) { if (partnerParams[i] != null && partnerParams[i + 1] != null) { String key = partnerParams[i]; @@ -793,7 +811,7 @@ class CommandExecutor { if (_command.containsParameter('callbackParams')) { List callbackParams = - _command.getParamteters('callbackParams')!; + _command.getParameters('callbackParams')!; for (int i = 0; i < callbackParams.length; i = i + 2) { if (callbackParams[i] != null && callbackParams[i + 1] != null) { String key = callbackParams[i]; @@ -803,7 +821,7 @@ class CommandExecutor { } } if (_command.containsParameter('partnerParams')) { - List partnerParams = _command.getParamteters('partnerParams')!; + List partnerParams = _command.getParameters('partnerParams')!; for (int i = 0; i < partnerParams.length; i = i + 2) { if (partnerParams[i] != null && partnerParams[i + 1] != null) { String key = partnerParams[i]; @@ -827,7 +845,7 @@ class CommandExecutor { if (_command.containsParameter('granularOptions')) { List granularOptions = - _command.getParamteters('granularOptions')!; + _command.getParameters('granularOptions')!; for (var i = 0; i < granularOptions.length; i += 3) { if (granularOptions[i] != null && granularOptions[i + 1] != null && granularOptions[i + 2] != null) { String partnerName = granularOptions[i]; @@ -840,7 +858,7 @@ class CommandExecutor { if (_command.containsParameter('partnerSharingSettings')) { List partnerSharingSettings = - _command.getParamteters('partnerSharingSettings')!; + _command.getParameters('partnerSharingSettings')!; for (var i = 0; i < partnerSharingSettings.length; i += 3) { if (partnerSharingSettings[i] != null && partnerSharingSettings[i + 1] != null && partnerSharingSettings[i + 2] != null) { String partnerName = partnerSharingSettings[i]; @@ -864,11 +882,11 @@ class CommandExecutor { AdjustAdRevenue adjustAdRevenue = new AdjustAdRevenue(source); if (_command.containsParameter('revenue')) { - List revenueParams = _command.getParamteters('revenue')!; + List revenueParams = _command.getParameters('revenue')!; adjustAdRevenue.setRevenue(num.parse(revenueParams[1]), revenueParams[0]); } if (_command.containsParameter('callbackParams')) { - List callbackParams = _command.getParamteters('callbackParams')!; + List callbackParams = _command.getParameters('callbackParams')!; for (int i = 0; i < callbackParams.length; i = i + 2) { if (callbackParams[i] != null && callbackParams[i + 1] != null) { String key = callbackParams[i]; @@ -878,7 +896,7 @@ class CommandExecutor { } } if (_command.containsParameter('partnerParams')) { - List partnerParams = _command.getParamteters('partnerParams')!; + List partnerParams = _command.getParameters('partnerParams')!; for (int i = 0; i < partnerParams.length; i = i + 2) { if (partnerParams[i] != null && partnerParams[i + 1] != null) { String key = partnerParams[i]; diff --git a/test/app/lib/main.dart b/test/app/lib/main.dart index c0455c8..ff103bf 100644 --- a/test/app/lib/main.dart +++ b/test/app/lib/main.dart @@ -1,22 +1,55 @@ import 'dart:io'; import 'package:adjust_sdk/adjust.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:test_app/command.dart'; import 'package:test_app/command_executor.dart'; import 'package:test_lib/test_lib.dart'; void main() { - runApp(MyApp()); + runApp(const AdjustTestApp()); } -class MyApp extends StatefulWidget { +/// main application widget for Adjust Flutter Test App +class AdjustTestApp extends StatelessWidget { + const AdjustTestApp({super.key}); + @override - _MyAppState createState() => _MyAppState(); + Widget build(BuildContext context) { + return MaterialApp( + title: 'Adjust Test App', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + useMaterial3: true, + ), + home: const TestHomePage(), + ); + } } -class _MyAppState extends State { +/// home page containing the test session controls +class TestHomePage extends StatefulWidget { + const TestHomePage({super.key}); + + @override + State createState() => _TestHomePageState(); +} + +class _TestHomePageState extends State { + // configuration constants + static const String _defaultAddress = '192.168.86.237'; + static const String _androidProtocol = 'https'; + static const String _androidPort = '8443'; + static const String _iosProtocol = 'http'; + static const String _iosPort = '8080'; + static const String _wsPort = '1987'; + + // adjust brand colors + static const Color _adjustNavy = Color(0xFF1B2951); + static const Color _adjustNavyLight = Color(0xFF2A3A5C); + String? _overwriteUrl; late String _controlUrl; late CommandExecutor _commandExecutor; @@ -24,103 +57,230 @@ class _MyAppState extends State { @override void initState() { super.initState(); + _initializeTestEnvironment(); + } + /// initialize the test environment with platform-specific URLs + void _initializeTestEnvironment() { if (Platform.isAndroid) { - String _address = '192.168.86.237'; - String _protocol = 'https'; - String _port = '8443'; - _overwriteUrl = _protocol + '://' + _address + ':' + _port; - _controlUrl = 'ws://' + _address + ':1987'; + _overwriteUrl = '$_androidProtocol://$_defaultAddress:$_androidPort'; } else { - String _address = '192.168.86.237'; - String _protocol = 'http'; - String _port = '8080'; - _overwriteUrl = _protocol + '://' + _address + ':' + _port; - _controlUrl = 'ws://' + _address + ':1987'; + _overwriteUrl = '$_iosProtocol://$_defaultAddress:$_iosPort'; } + _controlUrl = 'ws://$_defaultAddress:$_wsPort'; - // Initialise command executor. + // initialize command executor _commandExecutor = CommandExecutor(_overwriteUrl); - // Initialise test library. - TestLib.setExecuteCommandHalder((final dynamic callArgs) { - print('[AdjustTestApp]: executeCommandHandler pinged in test app!'); - Command command = Command(callArgs); - print( - '[AdjustTestApp]: Executing command ${command.className}.${command.methodName}'); - _commandExecutor.executeCommand(command); - }); + // initialize test library with command handler + TestLib.setExecuteCommandHalder(_handleTestCommand); TestLib.init(_overwriteUrl!, _controlUrl); } + /// handle incoming test commands + void _handleTestCommand(dynamic callArgs) { + print('[AdjustTestApp]: Command handler triggered'); + final command = Command(callArgs); + print('[AdjustTestApp]: Executing ${command.className}.${command.methodName}'); + _commandExecutor.executeCommand(command); + } + + /// start a new test session + void _startTestSession() { + Adjust.getSdkVersion().then((sdkVersion) { + print('[AdjustTestApp]: Starting test session with SDK version: $sdkVersion'); + TestLib.startTestSession(sdkVersion); + }); + } + @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Adjust Test App', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, + return Scaffold( + appBar: AppBar( + title: const Text( + 'Adjust Test App', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + centerTitle: true, + backgroundColor: _adjustNavy, + elevation: 0, ), - home: Scaffold( - appBar: AppBar( - title: const Text('Adjust Test App'), - centerTitle: true, - backgroundColor: const Color(0xFF1B2951), // Adjust dark navy color - foregroundColor: Colors.white, - elevation: 0, + body: Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [_adjustNavy, _adjustNavyLight], + ), ), - body: Container( - width: double.infinity, - height: double.infinity, - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0xFF1B2951), // Adjust dark navy - Color(0xFF2A3A5C), // Slightly lighter navy + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // app icon or logo space + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + ), + child: const Icon( + Icons.science_outlined, + size: 40, + color: Colors.white, + ), + ), + const SizedBox(height: 32), + + // title and description + const Text( + 'Test Session', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + Text( + 'Tap the button below to start testing\nthe Adjust SDK functionality', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.white.withOpacity(0.8), + height: 1.4, + ), + ), + const SizedBox(height: 48), + + // main action button + _buildStartButton(), + + const SizedBox(height: 24), + + // connection info + _buildConnectionInfo(), ], ), ), - child: Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: _buildActionButton( - 'Start Test Session', - () => Adjust.getSdkVersion().then((sdkVersion) { - TestLib.startTestSession(sdkVersion); - }), - ), - ), - ), ), ), ); } - Widget _buildActionButton(String text, VoidCallback onPressed) { + /// build the main start test session button + Widget _buildStartButton() { return SizedBox( width: double.infinity, - height: 50, + height: 56, child: ElevatedButton( - onPressed: onPressed, + onPressed: _startTestSession, style: ElevatedButton.styleFrom( backgroundColor: Colors.white, - foregroundColor: const Color(0xFF1B2951), + foregroundColor: _adjustNavy, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(25), + borderRadius: BorderRadius.circular(28), ), - elevation: 3, - shadowColor: Colors.black26, + elevation: 8, + shadowColor: Colors.black.withOpacity(0.3), ), - child: Text( - text, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), + child: const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.play_arrow_rounded, size: 24), + SizedBox(width: 8), + Text( + 'Start Test Session', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ); + } + + /// build connection information display + Widget _buildConnectionInfo() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.white.withOpacity(0.2), + width: 1, ), ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.info_outline, + size: 16, + color: Colors.white.withOpacity(0.7), + ), + const SizedBox(width: 8), + Text( + 'Connection Info', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.white.withOpacity(0.9), + ), + ), + ], + ), + const SizedBox(height: 8), + _buildInfoRow('Platform', Platform.isAndroid ? 'Android' : 'iOS'), + _buildInfoRow('Test URL', _overwriteUrl ?? 'Not set'), + _buildInfoRow('Control URL', _controlUrl), + ], + ), + ); + } + + /// build an information row + Widget _buildInfoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 80, + child: Text( + '$label:', + style: TextStyle( + fontSize: 12, + color: Colors.white.withOpacity(0.6), + ), + ), + ), + Expanded( + child: Text( + value, + style: TextStyle( + fontSize: 12, + color: Colors.white.withOpacity(0.8), + fontFamily: 'monospace', + ), + ), + ), + ], + ), ); } } diff --git a/test/app/pubspec.yaml b/test/app/pubspec.yaml index 9a7954f..33ab990 100644 --- a/test/app/pubspec.yaml +++ b/test/app/pubspec.yaml @@ -14,9 +14,6 @@ dependencies: test_lib: path: ../ - - - dev_dependencies: flutter_test: sdk: flutter diff --git a/test/app/test/widget_test.dart b/test/app/test/widget_test.dart deleted file mode 100644 index 1a53d78..0000000 --- a/test/app/test/widget_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:test_app/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From fb2014dc9da7666b5cc27aaae541ac341accbe50 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 15:47:44 +0200 Subject: [PATCH 07/17] refac: continue example app refac spree --- example/README.md | 81 +++- example/android/app/build.gradle.kts | 8 +- example/lib/main.dart | 680 ++++++++++++++++----------- 3 files changed, 489 insertions(+), 280 deletions(-) diff --git a/example/README.md b/example/README.md index 1b7a4e3..cb44ee3 100644 --- a/example/README.md +++ b/example/README.md @@ -1,3 +1,80 @@ -# example +# Adjust Flutter Example App -A new Flutter project. +A comprehensive demonstration application showcasing the full functionality of the Adjust Flutter SDK. This app provides interactive examples of all major SDK features and serves as a reference implementation. + +## Features + +- **Event Tracking**: Demonstrate simple, revenue, callback, and partner events +- **Device Information**: Access Google AdID, Adjust identifier, IDFA, and attribution data +- **SDK Management**: Toggle SDK state and check enabled status +- **Real-time Callbacks**: Live demonstration of attribution, session, and event callbacks +- **Modern UI**: Beautiful, intuitive interface following Adjust design guidelines + +## Getting Started + +1. **Prerequisites**: Ensure Flutter is installed and configured +2. **Install Dependencies**: Run `flutter pub get` in this directory +3. **Run the App**: Use `flutter run` to start the example application +4. **Explore Features**: Tap buttons to test different SDK functionalities + +## App Structure + +- **Event Tracking Section**: Test different types of events with various parameters +- **Device Information Section**: Retrieve device-specific identifiers and attribution +- **SDK Control Section**: Manage SDK state and verify functionality + +## SDK Configuration + +The app is configured with: +- **App Token**: `2fm9gkqubvpc` (sandbox environment) +- **Environment**: Sandbox mode for safe testing +- **Log Level**: Verbose logging for detailed insights +- **Callbacks**: Comprehensive callback setup for all event types + +## Event Tokens + +The following test event tokens are configured: +- **Simple Event**: `g3mfiw` - Basic event tracking +- **Revenue Event**: `a4fd35` - Event with revenue data +- **Callback Event**: `34vgg9` - Event with callback parameters +- **Partner Event**: `w788qs` - Event with partner parameters + +## Usage Examples + +### Track a Simple Event +```dart +final event = AdjustEvent('g3mfiw'); +Adjust.trackEvent(event); +``` + +### Track Revenue Event +```dart +final event = AdjustEvent('a4fd35'); +event.setRevenue(100.0, 'EUR'); +event.transactionId = 'DummyTransactionId'; +Adjust.trackEvent(event); +``` + +### Get Attribution Data +```dart +Adjust.getAttribution().then((attribution) { + // Handle attribution data +}); +``` + +## Callbacks + +The app demonstrates all available callback types: +- **Attribution Callback**: Triggered when attribution data changes +- **Session Success/Failure**: Monitor session tracking status +- **Event Success/Failure**: Track event delivery status +- **Deferred Deeplinks**: Handle deferred deeplink scenarios +- **SKAN Updates**: iOS StoreKit Ad Network updates + +## Testing + +Use this app to: +- Verify SDK integration in your development environment +- Test different event tracking scenarios +- Understand callback behavior and data structure +- Validate attribution and device identifier retrieval diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts index d16e79d..ab881a4 100644 --- a/example/android/app/build.gradle.kts +++ b/example/android/app/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("com.android.application") id("kotlin-android") - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + // the Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins id("dev.flutter.flutter-gradle-plugin") } @@ -20,10 +20,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.adjust.examples" - // You can update the following values to match your application needs. - // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode @@ -32,8 +29,7 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. + // signing with debug keys for development convenience signingConfig = signingConfigs.getByName("debug") } } diff --git a/example/lib/main.dart b/example/lib/main.dart index 1b28c65..ef3051d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -13,16 +13,19 @@ void main() { runApp(const AdjustExampleApp()); } +/// main application widget for Adjust Flutter Example App class AdjustExampleApp extends StatelessWidget { - const AdjustExampleApp({Key? key}) : super(key: key); + const AdjustExampleApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Adjust Example', + debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, + useMaterial3: true, scaffoldBackgroundColor: const Color(0xFFF5F5F5), ), home: const HomePage(), @@ -30,22 +33,33 @@ class AdjustExampleApp extends StatelessWidget { } } +/// home page showcasing Adjust SDK functionality class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); + const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State with WidgetsBindingObserver { - // Event tokens - static const String eventTokenSimple = 'g3mfiw'; - static const String eventTokenRevenue = 'a4fd35'; - static const String eventTokenCallback = '34vgg9'; - static const String eventTokenPartner = 'w788qs'; + // event tokens for demonstration + static const String _eventTokenSimple = 'g3mfiw'; + static const String _eventTokenRevenue = 'a4fd35'; + static const String _eventTokenCallback = '34vgg9'; + static const String _eventTokenPartner = 'w788qs'; - // Button text for SDK toggle + // adjust configuration constants + static const String _appToken = '2fm9gkqubvpc'; + static const AdjustEnvironment _environment = AdjustEnvironment.sandbox; + static const AdjustLogLevel _logLevel = AdjustLogLevel.verbose; + + // adjust brand colors + static const Color _adjustNavy = Color(0xFF1B2951); + static const Color _adjustNavyLight = Color(0xFF2A3A5C); + + // ui state String _toggleButtonText = 'Toggle SDK'; + bool _isLoading = false; @override void initState() { @@ -61,288 +75,388 @@ class _HomePageState extends State with WidgetsBindingObserver { super.dispose(); } + /// initialize the Adjust SDK with comprehensive configuration Future _initializeAdjustSdk() async { - final config = AdjustConfig('2fm9gkqubvpc', AdjustEnvironment.sandbox); - config.logLevel = AdjustLogLevel.verbose; - - // Set up callbacks - config.attributionCallback = (AdjustAttribution attribution) { - print('[Adjust]: Attribution changed!'); - if (attribution.trackerToken != null) { - print('[Adjust]: Tracker token: ${attribution.trackerToken}'); - } - if (attribution.trackerName != null) { - print('[Adjust]: Tracker name: ${attribution.trackerName}'); - } - if (attribution.campaign != null) { - print('[Adjust]: Campaign: ${attribution.campaign}'); - } - if (attribution.network != null) { - print('[Adjust]: Network: ${attribution.network}'); - } - if (attribution.creative != null) { - print('[Adjust]: Creative: ${attribution.creative}'); - } - if (attribution.adgroup != null) { - print('[Adjust]: Adgroup: ${attribution.adgroup}'); - } - if (attribution.clickLabel != null) { - print('[Adjust]: Click label: ${attribution.clickLabel}'); - } - if (attribution.costType != null) { - print('[Adjust]: Cost type: ${attribution.costType}'); - } - if (attribution.costAmount != null) { - print('[Adjust]: Cost amount: ${attribution.costAmount}'); - } - if (attribution.costCurrency != null) { - print('[Adjust]: Cost currency: ${attribution.costCurrency}'); - } - if (attribution.jsonResponse != null) { - print('[Adjust]: JSON response: ${attribution.jsonResponse}'); - } - if (attribution.fbInstallReferrer != null) { - print('[Adjust]: FB install referrer: ${attribution.fbInstallReferrer}'); - } - }; - - config.sessionSuccessCallback = (AdjustSessionSuccess sessionSuccess) { - print('[Adjust]: Session tracking success!'); - if (sessionSuccess.message != null) { - print('[Adjust]: Message: ${sessionSuccess.message}'); - } - if (sessionSuccess.timestamp != null) { - print('[Adjust]: Timestamp: ${sessionSuccess.timestamp}'); - } - if (sessionSuccess.adid != null) { - print('[Adjust]: Adid: ${sessionSuccess.adid}'); - } - if (sessionSuccess.jsonResponse != null) { - print('[Adjust]: JSON response: ${sessionSuccess.jsonResponse}'); - } - }; - - config.sessionFailureCallback = (AdjustSessionFailure sessionFailure) { - print('[Adjust]: Session tracking failure!'); - if (sessionFailure.message != null) { - print('[Adjust]: Message: ${sessionFailure.message}'); - } - if (sessionFailure.timestamp != null) { - print('[Adjust]: Timestamp: ${sessionFailure.timestamp}'); - } - if (sessionFailure.adid != null) { - print('[Adjust]: Adid: ${sessionFailure.adid}'); - } - if (sessionFailure.willRetry != null) { - print('[Adjust]: Will retry: ${sessionFailure.willRetry}'); - } - if (sessionFailure.jsonResponse != null) { - print('[Adjust]: JSON response: ${sessionFailure.jsonResponse}'); - } - }; - - config.eventSuccessCallback = (AdjustEventSuccess eventSuccess) { - print('[Adjust]: Event tracking success!'); - if (eventSuccess.eventToken != null) { - print('[Adjust]: Event token: ${eventSuccess.eventToken}'); - } - if (eventSuccess.message != null) { - print('[Adjust]: Message: ${eventSuccess.message}'); - } - if (eventSuccess.timestamp != null) { - print('[Adjust]: Timestamp: ${eventSuccess.timestamp}'); - } - if (eventSuccess.adid != null) { - print('[Adjust]: Adid: ${eventSuccess.adid}'); - } - if (eventSuccess.callbackId != null) { - print('[Adjust]: Callback ID: ${eventSuccess.callbackId}'); - } - if (eventSuccess.jsonResponse != null) { - print('[Adjust]: JSON response: ${eventSuccess.jsonResponse}'); - } - }; - - config.eventFailureCallback = (AdjustEventFailure eventFailure) { - print('[Adjust]: Event tracking failure!'); - if (eventFailure.eventToken != null) { - print('[Adjust]: Event token: ${eventFailure.eventToken}'); - } - if (eventFailure.message != null) { - print('[Adjust]: Message: ${eventFailure.message}'); - } - if (eventFailure.timestamp != null) { - print('[Adjust]: Timestamp: ${eventFailure.timestamp}'); - } - if (eventFailure.adid != null) { - print('[Adjust]: Adid: ${eventFailure.adid}'); - } - if (eventFailure.callbackId != null) { - print('[Adjust]: Callback ID: ${eventFailure.callbackId}'); - } - if (eventFailure.willRetry != null) { - print('[Adjust]: Will retry: ${eventFailure.willRetry}'); - } - if (eventFailure.jsonResponse != null) { - print('[Adjust]: JSON response: ${eventFailure.jsonResponse}'); - } - }; - - config.deferredDeeplinkCallback = (String? uri) { - print('[Adjust]: Received deferred deeplink: $uri'); - }; - - config.skanUpdatedCallback = (Map skanData) { - print('[Adjust]: Received SKAN update information!'); - if (skanData["conversion_value"] != null) { - print('[Adjust]: Conversion value: ${skanData["conversion_value"]}'); - } - if (skanData["coarse_value"] != null) { - print('[Adjust]: Coarse value: ${skanData["coarse_value"]}'); - } - if (skanData["lock_window"] != null) { - print('[Adjust]: Lock window: ${skanData["lock_window"]}'); - } - if (skanData["error"] != null) { - print('[Adjust]: Error: ${skanData["error"]}'); - } - }; + setState(() => _isLoading = true); + + try { + final config = AdjustConfig(_appToken, _environment); + config.logLevel = _logLevel; + + // configure attribution callback + config.attributionCallback = _handleAttributionCallback; + + // configure session callbacks + config.sessionSuccessCallback = _handleSessionSuccess; + config.sessionFailureCallback = _handleSessionFailure; + + // configure event callbacks + config.eventSuccessCallback = _handleEventSuccess; + config.eventFailureCallback = _handleEventFailure; + + // configure deeplink callback + config.deferredDeeplinkCallback = _handleDeferredDeeplink; + + // configure SKAN callback + config.skanUpdatedCallback = _handleSkanUpdate; + + // setup global parameters for demonstration + _setupGlobalParameters(); + + // initialize the SDK + Adjust.initSdk(config); + + print('[AdjustExample]: SDK initialized successfully'); + } catch (e) { + print('[AdjustExample]: Failed to initialize SDK: $e'); + } finally { + setState(() => _isLoading = false); + } + } - // Global parameters + /// setup global callback and partner parameters + void _setupGlobalParameters() { + // add global parameters Adjust.addGlobalCallbackParameter('scp_foo_1', 'scp_bar'); Adjust.addGlobalCallbackParameter('scp_foo_2', 'scp_value'); Adjust.addGlobalPartnerParameter('spp_foo_1', 'spp_bar'); Adjust.addGlobalPartnerParameter('spp_foo_2', 'spp_value'); + + // demonstrate parameter removal Adjust.removeGlobalCallbackParameter('scp_foo_1'); Adjust.removeGlobalPartnerParameter('spp_foo_1'); + + // clear all parameters (for demonstration) Adjust.removeGlobalCallbackParameters(); Adjust.removeGlobalPartnerParameters(); + } + + /// handle attribution updates + void _handleAttributionCallback(AdjustAttribution attribution) { + print('[AdjustExample]: Attribution changed!'); + + final attributionData = { + 'Tracker token': attribution.trackerToken, + 'Tracker name': attribution.trackerName, + 'Campaign': attribution.campaign, + 'Network': attribution.network, + 'Creative': attribution.creative, + 'Adgroup': attribution.adgroup, + 'Click label': attribution.clickLabel, + 'Cost type': attribution.costType, + 'Cost amount': attribution.costAmount?.toString(), + 'Cost currency': attribution.costCurrency, + 'FB install referrer': attribution.fbInstallReferrer, + }; + + attributionData.forEach((key, value) { + if (value != null) { + print('[AdjustExample]: $key: $value'); + } + }); + + if (attribution.jsonResponse != null) { + print('[AdjustExample]: JSON response: ${attribution.jsonResponse}'); + } + } + + /// handle successful session tracking + void _handleSessionSuccess(AdjustSessionSuccess sessionSuccess) { + print('[AdjustExample]: Session tracking success!'); + _logSessionData('Success', sessionSuccess.message, sessionSuccess.timestamp, + sessionSuccess.adid, sessionSuccess.jsonResponse); + } + + /// handle failed session tracking + void _handleSessionFailure(AdjustSessionFailure sessionFailure) { + print('[AdjustExample]: Session tracking failure!'); + _logSessionData('Failure', sessionFailure.message, sessionFailure.timestamp, + sessionFailure.adid, sessionFailure.jsonResponse); + + if (sessionFailure.willRetry != null) { + print('[AdjustExample]: Will retry: ${sessionFailure.willRetry}'); + } + } + + /// helper method to log session data + void _logSessionData(String type, String? message, String? timestamp, + String? adid, String? jsonResponse) { + if (message != null) print('[AdjustExample]: Message: $message'); + if (timestamp != null) print('[AdjustExample]: Timestamp: $timestamp'); + if (adid != null) print('[AdjustExample]: Adid: $adid'); + if (jsonResponse != null) print('[AdjustExample]: JSON response: $jsonResponse'); + } + + /// handle successful event tracking + void _handleEventSuccess(AdjustEventSuccess eventSuccess) { + print('[AdjustExample]: Event tracking success!'); + _logEventData('Success', eventSuccess.eventToken, eventSuccess.message, + eventSuccess.timestamp, eventSuccess.adid, eventSuccess.callbackId, + eventSuccess.jsonResponse, null); + } + + /// handle failed event tracking + void _handleEventFailure(AdjustEventFailure eventFailure) { + print('[AdjustExample]: Event tracking failure!'); + _logEventData('Failure', eventFailure.eventToken, eventFailure.message, + eventFailure.timestamp, eventFailure.adid, eventFailure.callbackId, + eventFailure.jsonResponse, eventFailure.willRetry); + } + + /// helper method to log event data + void _logEventData(String type, String? eventToken, String? message, + String? timestamp, String? adid, String? callbackId, + String? jsonResponse, bool? willRetry) { + if (eventToken != null) print('[AdjustExample]: Event token: $eventToken'); + if (message != null) print('[AdjustExample]: Message: $message'); + if (timestamp != null) print('[AdjustExample]: Timestamp: $timestamp'); + if (adid != null) print('[AdjustExample]: Adid: $adid'); + if (callbackId != null) print('[AdjustExample]: Callback ID: $callbackId'); + if (willRetry != null) print('[AdjustExample]: Will retry: $willRetry'); + if (jsonResponse != null) print('[AdjustExample]: JSON response: $jsonResponse'); + } + + /// handle deferred deeplinks + void _handleDeferredDeeplink(String? uri) { + print('[AdjustExample]: Received deferred deeplink: $uri'); + } - // Request tracking authorization - // Adjust.requestAppTrackingAuthorization().then((status) { - // print('[Adjust]: Authorization status update!'); - // switch (status.toInt()) { - // case 0: - // print('[Adjust]: ATTrackingManagerAuthorizationStatusNotDetermined'); - // break; - // case 1: - // print('[Adjust]: ATTrackingManagerAuthorizationStatusRestricted'); - // break; - // case 2: - // print('[Adjust]: ATTrackingManagerAuthorizationStatusDenied'); - // break; - // case 3: - // print('[Adjust]: ATTrackingManagerAuthorizationStatusAuthorized'); - // break; - // } - // }); - - // Initialize SDK - Adjust.initSdk(config); + /// handle SKAN updates + void _handleSkanUpdate(Map skanData) { + print('[AdjustExample]: Received SKAN update information!'); + + final skanFields = { + 'conversion_value': 'Conversion value', + 'coarse_value': 'Coarse value', + 'lock_window': 'Lock window', + 'error': 'Error', + }; + + skanFields.forEach((key, description) { + if (skanData[key] != null) { + print('[AdjustExample]: $description: ${skanData[key]}'); + } + }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Adjust Example'), + title: const Text( + 'Adjust Example', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), centerTitle: true, - backgroundColor: const Color(0xFF1B2951), // Adjust dark navy color - foregroundColor: Colors.white, + backgroundColor: _adjustNavy, elevation: 0, ), body: Container( width: double.infinity, - padding: const EdgeInsets.all(16.0), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [ - Color(0xFF1B2951), // Adjust dark navy - Color(0xFF2A3A5C), // Slightly lighter navy - ], + colors: [_adjustNavy, _adjustNavyLight], ), ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildActionButton('Track Simple Event', _trackSimpleEvent), - const SizedBox(height: 12), - _buildActionButton('Track Revenue Event', _trackRevenueEvent), - const SizedBox(height: 12), - _buildActionButton('Track Callback Event', _trackCallbackEvent), - const SizedBox(height: 12), - _buildActionButton('Track Partner Event', _trackPartnerEvent), - const SizedBox(height: 12), - _buildActionButton('Get Google AdId', _getGoogleAdId), - const SizedBox(height: 12), - _buildActionButton('Get Adjust Identifier', _getAdjustIdentifier), - const SizedBox(height: 12), - _buildActionButton('Get IDFA', _getIdfa), - const SizedBox(height: 12), - _buildActionButton('Get Attribution', _getAttribution), - const SizedBox(height: 12), - _buildActionButton(_toggleButtonText, _toggleSdkState), - const SizedBox(height: 12), - _buildActionButton('Is Enabled?', _checkIfSdkEnabled), - ], + child: SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + // app header section + _buildHeaderSection(), + const SizedBox(height: 32), + + // loading indicator or action buttons + if (_isLoading) + _buildLoadingSection() + else + _buildActionButtons(), + ], + ), + ), + ), + ), + ); + } + + /// build the header section with app icon and description + Widget _buildHeaderSection() { + return Column( + children: [ + // app icon + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + ), + child: const Icon( + Icons.analytics_outlined, + size: 40, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + + // app title and description + const Text( + 'Adjust SDK Demo', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.white, + ), ), + const SizedBox(height: 8), + Text( + 'Explore the full functionality of the Adjust SDK\nwith this comprehensive example application', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.white.withOpacity(0.8), + height: 1.4, + ), + ), + ], + ); + } + + /// build loading section + Widget _buildLoadingSection() { + return Column( + children: [ + const CircularProgressIndicator( + color: Colors.white, + ), + const SizedBox(height: 16), + Text( + 'Initializing Adjust SDK...', + style: TextStyle( + color: Colors.white.withOpacity(0.8), + fontSize: 16, + ), + ), + ], + ); + } + + /// build all action buttons in organized sections + Widget _buildActionButtons() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // event tracking section + _buildSectionHeader('Event Tracking'), + const SizedBox(height: 16), + _buildActionButton('Track Simple Event', Icons.touch_app, _trackSimpleEvent), + const SizedBox(height: 12), + _buildActionButton('Track Revenue Event', Icons.monetization_on, _trackRevenueEvent), + const SizedBox(height: 12), + _buildActionButton('Track Callback Event', Icons.call_made, _trackCallbackEvent), + const SizedBox(height: 12), + _buildActionButton('Track Partner Event', Icons.handshake, _trackPartnerEvent), + + const SizedBox(height: 32), + + // device information section + _buildSectionHeader('Device Information'), + const SizedBox(height: 16), + _buildActionButton('Get Google AdId', Icons.android, _getGoogleAdId), + const SizedBox(height: 12), + _buildActionButton('Get Adjust Identifier', Icons.fingerprint, _getAdjustIdentifier), + const SizedBox(height: 12), + _buildActionButton('Get IDFA', Icons.phone_iphone, _getIdfa), + const SizedBox(height: 12), + _buildActionButton('Get Attribution', Icons.analytics, _getAttribution), + + const SizedBox(height: 32), + + // SDK control section + _buildSectionHeader('SDK Control'), + const SizedBox(height: 16), + _buildActionButton(_toggleButtonText, Icons.power_settings_new, _toggleSdkState), + const SizedBox(height: 12), + _buildActionButton('Is Enabled?', Icons.help_outline, _checkIfSdkEnabled), + ], + ); + } + + /// build section header + Widget _buildSectionHeader(String title) { + return Text( + title, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Colors.white, ), ); } - Widget _buildActionButton(String text, VoidCallback onPressed) { + /// build enhanced action button with icon + Widget _buildActionButton(String text, IconData icon, VoidCallback onPressed) { return SizedBox( width: double.infinity, - height: 50, - child: ElevatedButton( + height: 56, + child: ElevatedButton.icon( onPressed: onPressed, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: const Color(0xFF1B2951), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(25), - ), - elevation: 3, - shadowColor: Colors.black26, - ), - child: Text( + icon: Icon(icon, size: 20), + label: Text( text, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: _adjustNavy, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28), + ), + elevation: 8, + shadowColor: Colors.black.withOpacity(0.3), + ), ), ); } + /// update toggle button text based on SDK state void _updateToggleButtonText() { try { Adjust.isEnabled().then((isEnabled) { - setState(() { - _toggleButtonText = isEnabled ? 'Disable SDK' : 'Enable SDK'; - }); + if (mounted) { + setState(() { + _toggleButtonText = isEnabled ? 'Disable SDK' : 'Enable SDK'; + }); + } }); } on PlatformException { - // Keep default text if method not found + // keep default text if method not found } } + /// toggle SDK enabled/disabled state void _toggleSdkState() { try { Adjust.isEnabled().then((isEnabled) { if (isEnabled) { Adjust.disable(); - print('[Adjust]: SDK disabled'); + print('[AdjustExample]: SDK disabled'); _showDialog('SDK State', 'Adjust SDK has been disabled'); } else { Adjust.enable(); - print('[Adjust]: SDK enabled'); + print('[AdjustExample]: SDK enabled'); _showDialog('SDK State', 'Adjust SDK has been enabled'); } - // Update button text after toggle + // update button text after toggle _updateToggleButtonText(); }); } on PlatformException { @@ -350,6 +464,7 @@ class _HomePageState extends State with WidgetsBindingObserver { } } + /// check if SDK is currently enabled void _checkIfSdkEnabled() { try { Adjust.isEnabled().then((isEnabled) { @@ -360,108 +475,129 @@ class _HomePageState extends State with WidgetsBindingObserver { } } + /// track a simple event without additional parameters void _trackSimpleEvent() { - final event = AdjustEvent(eventTokenSimple); + final event = AdjustEvent(_eventTokenSimple); Adjust.trackEvent(event); + print('[AdjustExample]: Simple event tracked'); } + /// track a revenue event with transaction details void _trackRevenueEvent() { - final event = AdjustEvent(eventTokenRevenue); + final event = AdjustEvent(_eventTokenRevenue); event.setRevenue(100.0, 'EUR'); event.transactionId = 'DummyTransactionId'; Adjust.trackEvent(event); + print('[AdjustExample]: Revenue event tracked (€100.00)'); } + /// track an event with callback parameters void _trackCallbackEvent() { - final event = AdjustEvent(eventTokenCallback); + final event = AdjustEvent(_eventTokenCallback); event.addCallbackParameter('key1', 'value1'); event.addCallbackParameter('key2', 'value2'); Adjust.trackEvent(event); + print('[AdjustExample]: Callback event tracked with parameters'); } + /// track an event with partner parameters void _trackPartnerEvent() { - final event = AdjustEvent(eventTokenPartner); + final event = AdjustEvent(_eventTokenPartner); event.addPartnerParameter('foo1', 'bar1'); event.addPartnerParameter('foo2', 'bar2'); Adjust.trackEvent(event); + print('[AdjustExample]: Partner event tracked with parameters'); } + /// get Google Advertising ID void _getGoogleAdId() { Adjust.getGoogleAdId().then((googleAdId) { - _showDialog('Get Google Advertising Id', 'Received Google Advertising Id: $googleAdId'); + _showDialog('Google Advertising ID', 'Received Google Advertising Id:\n$googleAdId'); }); } + /// get Adjust identifier void _getAdjustIdentifier() { Adjust.getAdid().then((adid) { - _showDialog('Adjust Identifier', 'Received Adjust identifier: $adid'); + _showDialog('Adjust Identifier', 'Received Adjust identifier:\n$adid'); }); } + /// get IDFA (iOS) void _getIdfa() { Adjust.getIdfa().then((idfa) { - _showDialog('IDFA', 'Received IDFA: $idfa'); + _showDialog('IDFA', 'Received IDFA:\n$idfa'); }); } + /// get current attribution information void _getAttribution() { Adjust.getAttribution().then((attribution) { - String attributionInfo = 'Attribution data:\n'; - if (attribution.trackerToken != null) { - attributionInfo += 'Tracker token: ${attribution.trackerToken}\n'; - } - if (attribution.trackerName != null) { - attributionInfo += 'Tracker name: ${attribution.trackerName}\n'; - } - if (attribution.campaign != null) { - attributionInfo += 'Campaign: ${attribution.campaign}\n'; - } - if (attribution.network != null) { - attributionInfo += 'Network: ${attribution.network}\n'; - } - if (attribution.creative != null) { - attributionInfo += 'Creative: ${attribution.creative}\n'; - } - if (attribution.adgroup != null) { - attributionInfo += 'Adgroup: ${attribution.adgroup}\n'; - } - if (attribution.clickLabel != null) { - attributionInfo += 'Click label: ${attribution.clickLabel}\n'; - } - if (attribution.costType != null) { - attributionInfo += 'Cost type: ${attribution.costType}\n'; - } - if (attribution.costAmount != null) { - attributionInfo += 'Cost amount: ${attribution.costAmount}\n'; - } - if (attribution.costCurrency != null) { - attributionInfo += 'Cost currency: ${attribution.costCurrency}\n'; - } + final attributionFields = { + 'Tracker token': attribution.trackerToken, + 'Tracker name': attribution.trackerName, + 'Campaign': attribution.campaign, + 'Network': attribution.network, + 'Creative': attribution.creative, + 'Adgroup': attribution.adgroup, + 'Click label': attribution.clickLabel, + 'Cost type': attribution.costType, + 'Cost amount': attribution.costAmount?.toString(), + 'Cost currency': attribution.costCurrency, + 'FB install referrer': attribution.fbInstallReferrer, + }; + + String attributionInfo = 'Attribution data:\n\n'; + attributionFields.forEach((key, value) { + if (value != null) { + attributionInfo += '$key: $value\n'; + } + }); + if (attribution.jsonResponse != null) { - attributionInfo += 'JSON response: ${attribution.jsonResponse}\n'; - } - if (attribution.fbInstallReferrer != null) { - attributionInfo += 'FB install referrer: ${attribution.fbInstallReferrer}\n'; + attributionInfo += '\nJSON response: ${attribution.jsonResponse}'; } _showDialog('Attribution', attributionInfo); }); } + /// show information dialog with enhanced styling void _showDialog(String title, String message) { - print(message); + print('[AdjustExample]: $title - $message'); + showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: Text(title), - content: Text(message), + title: Text( + title, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: _adjustNavy, + ), + ), + content: SingleChildScrollView( + child: Text( + message, + style: const TextStyle(fontSize: 14), + ), + ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('OK'), + style: TextButton.styleFrom( + foregroundColor: _adjustNavy, + ), + child: const Text( + 'OK', + style: TextStyle(fontWeight: FontWeight.w600), + ), ), ], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), ); }, ); From d3a5b5d21cfb0bf0a6ed76ec9ca938a3358e7a35 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 16:02:10 +0200 Subject: [PATCH 08/17] refac: update .gitignores --- .gitignore | 10 +- example/android/.gitignore | 1 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes test/.gitignore | 7 +- test/app/.gitignore | 109 ++---------- test/app/android/.gitignore | 8 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- test/app/android/gradlew | 160 ++++++++++++++++++ test/app/android/gradlew.bat | 90 ++++++++++ test/app/ios/.gitignore | 3 + 11 files changed, 291 insertions(+), 99 deletions(-) create mode 100644 example/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 test/app/android/gradle/wrapper/gradle-wrapper.jar create mode 100755 test/app/android/gradlew create mode 100644 test/app/android/gradlew.bat diff --git a/.gitignore b/.gitignore index 8e123bb..5f3da6b 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,12 @@ app.*.symbols !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock \ No newline at end of file +!/dev/ci/**/Gemfile.lock + +# Allow gradlew files for example and test apps so they can be run after cloning +!example/android/gradlew +!example/android/gradlew.bat +!example/android/gradle/wrapper/gradle-wrapper.jar +!test/app/android/gradlew +!test/app/android/gradlew.bat +!test/app/android/gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/example/android/.gitignore b/example/android/.gitignore index da7bcd2..be82512 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -1,4 +1,3 @@ -gradle-wrapper.jar /.gradle /captures/ /local.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ8zCNKueM5c!$)^mKpZNR!eIlFST57ePGQcqCqedAQ3UaUEzpjM--5V4YO zY22VxQm%$2NDnwfK+jkz=i2>NjAM6&P1DdcO<*Xs1-lzdXWn#LGSxwhPH7N%D8-zCgpFWt@`LgNYI+Fh^~nSiQmwH0^>E>*O$47MqfQza@Ce z1wBw;igLc#V2@y-*~Hp?jA1)+MYYyAt|DV_8RQCrRY@sAviO}wv;3gFdO>TE(=9o? z=S(r=0oT`w24=ihA=~iFV5z$ZG74?rmYn#eanx(!Hkxcr$*^KRFJKYYB&l6$WVsJ^ z-Iz#HYmE)Da@&seqG1fXsTER#adA&OrD2-T(z}Cwby|mQf{0v*v3hq~pzF`U`jenT z=XHXeB|fa?Ws$+9ADO0rco{#~+`VM?IXg7N>M0w1fyW1iiKTA@p$y zSiAJ%-Mg{m>&S4r#Tw@?@7ck}#oFo-iZJCWc`hw_J$=rw?omE{^tc59ftd`xq?jzf zo0bFUI=$>O!45{!c4?0KsJmZ#$vuYpZLo_O^oHTmmLMm0J_a{Nn`q5tG1m=0ecv$T z5H7r0DZGl6be@aJ+;26EGw9JENj0oJ5K0=^f-yBW2I0jqVIU};NBp*gF7_KlQnhB6 z##d$H({^HXj@il`*4^kC42&3)(A|tuhs;LygA-EWFSqpe+%#?6HG6}mE215Z4mjO2 zY2^?5$<8&k`O~#~sSc5Fy`5hg5#e{kG>SAbTxCh{y32fHkNryU_c0_6h&$zbWc63T z7|r?X7_H!9XK!HfZ+r?FvBQ$x{HTGS=1VN<>Ss-7M3z|vQG|N}Frv{h-q623@Jz*@ ziXlZIpAuY^RPlu&=nO)pFhML5=ut~&zWDSsn%>mv)!P1|^M!d5AwmSPIckoY|0u9I zTDAzG*U&5SPf+@c_tE_I!~Npfi$?gX(kn=zZd|tUZ_ez(xP+)xS!8=k(<{9@<+EUx zYQgZhjn(0qA#?~Q+EA9oh_Jx5PMfE3#KIh#*cFIFQGi)-40NHbJO&%ZvL|LAqU=Rw zf?Vr4qkUcKtLr^g-6*N-tfk+v8@#Lpl~SgKyH!+m9?T8B>WDWK22;!i5&_N=%f{__ z-LHb`v-LvKqTJZCx~z|Yg;U_f)VZu~q7trb%C6fOKs#eJosw&b$nmwGwP;Bz`=zK4 z>U3;}T_ptP)w=vJaL8EhW;J#SHA;fr13f=r#{o)`dRMOs-T;lp&Toi@u^oB_^pw=P zp#8Geo2?@!h2EYHY?L;ayT}-Df0?TeUCe8Cto{W0_a>!7Gxmi5G-nIIS;X{flm2De z{SjFG%knZoVa;mtHR_`*6)KEf=dvOT3OgT7C7&-4P#4X^B%VI&_57cBbli()(%zZC?Y0b;?5!f22UleQ=9h4_LkcA!Xsqx@q{ko&tvP_V@7epFs}AIpM{g??PA>U(sk$Gum>2Eu zD{Oy{$OF%~?B6>ixQeK9I}!$O0!T3#Ir8MW)j2V*qyJ z8Bg17L`rg^B_#rkny-=<3fr}Y42+x0@q6POk$H^*p3~Dc@5uYTQ$pfaRnIT}Wxb;- zl!@kkZkS=l)&=y|21veY8yz$t-&7ecA)TR|=51BKh(@n|d$EN>18)9kSQ|GqP?aeM ztXd9C&Md$PPF*FVs*GhoHM2L@D$(Qf%%x zwQBUt!jM~GgwluBcwkgwQ!249uPkNz3u@LSYZgmpHgX|P#8!iKk^vSKZ;?)KE$92d z2U>y}VWJ0&zjrIqddM3dz-nU%>bL&KU%SA|LiiUU7Ka|c=jF|vQ1V)Jz`JZe*j<5U6~RVuBEVJoY~ z&GE+F$f>4lN=X4-|9v*5O*Os>>r87u z!_1NSV?_X&HeFR1fOFb8_P)4lybJ6?1BWK`Tv2;4t|x1<#@17UO|hLGnrB%nu)fDk zfstJ4{X4^Y<8Lj<}g2^kksSefQTMuTo?tJLCh zC~>CR#a0hADw!_Vg*5fJwV{~S(j8)~sn>Oyt(ud2$1YfGck77}xN@3U_#T`q)f9!2 zf>Ia;Gwp2_C>WokU%(z2ec8z94pZyhaK+e>3a9sj^-&*V494;p9-xk+u1Jn#N_&xs z59OI2w=PuTErv|aNcK*>3l^W*p3}fjXJjJAXtBA#%B(-0--s;1U#f8gFYW!JL+iVG zV0SSx5w8eVgE?3Sg@eQv)=x<+-JgpVixZQNaZr}3b8sVyVs$@ndkF5FYKka@b+YAh z#nq_gzlIDKEs_i}H4f)(VQ!FSB}j>5znkVD&W0bOA{UZ7h!(FXrBbtdGA|PE1db>s z$!X)WY)u#7P8>^7Pjjj-kXNBuJX3(pJVetTZRNOnR5|RT5D>xmwxhAn)9KF3J05J; z-Mfb~dc?LUGqozC2p!1VjRqUwwDBnJhOua3vCCB-%ykW_ohSe?$R#dz%@Gym-8-RA zjMa_SJSzIl8{9dV+&63e9$4;{=1}w2=l+_j_Dtt@<(SYMbV-18&%F@Zl7F_5! z@xwJ0wiDdO%{}j9PW1(t+8P7Ud79yjY>x>aZYWJL_NI?bI6Y02`;@?qPz_PRqz(7v``20`- z033Dy|4;y6di|>cz|P-z|6c&3f&g^OAt8aN0Zd&0yZ>dq2aFCsE<~Ucf$v{sL=*++ zBxFSa2lfA+Y%U@B&3D=&CBO&u`#*nNc|PCY7XO<}MnG0VR764XrHtrb5zwC*2F!Lp zE<~Vj0;z!S-|3M4DFxuQ=`ShTf28<9p!81(0hFbGNqF%0gg*orez9!qt8e%o@Yfl@ zhvY}{@3&f??}7<`p>FyU;7?VkKbh8_=csozU=|fH&szgZ{=NDCylQ>EH^x5!K3~-V z)_2Y>0uJ`Z0Pb58y`RL+&n@m9tJ)O<%q#&u#DAIt+-rRt0eSe1MTtMl@W)H$b3D)@ z*A-1bUgZI)>HdcI4&W>P4W5{-j=s5p5`cbQ+{(g0+RDnz!TR^mxSLu_y#SDVKrj8i zA^hi6>jMGM;`$9Vfb-Yf!47b)Ow`2OKtNB=z|Kxa$5O}WPo;(Dc^`q(7X8kkeFyO8 z{XOq^07=u|7*P2`m;>PIFf=i80MKUxsN{d2cX0M+REsE*20+WQ79T9&cqT>=I_U% z{=8~^Isg(Nzo~`4iQfIb_#CVCD>#5h>=-Z#5dH}WxYzn%0)GAm6L2WdUdP=0_h>7f z(jh&7%1i(ZOn+}D8$iGK4Vs{pmHl_w4Qm-46H9>4^{3dz^DZDh+dw)6Xd@CpQNK$j z{CU;-cmpK=egplZ3y3%y=sEnCJ^eYVKXzV8H2_r*fJ*%*B;a1_lOpt6)IT1IAK2eB z{rie|uDJUrbgfUE>~C>@RO|m5ex55F{=~Bb4Cucp{ok7Yf9V}QuZ`#Gc|WaqsQlK- zKaV)iMRR__&Ak2Z=IM9R9g5$WM4u{a^C-7uX*!myEym z#_#p^T!P~#Dx$%^K>Y_nj_3J*E_LwJ60-5Xu=LkJAwcP@|0;a&+|+ZX`Jbj9P5;T% z|KOc}4*#4o{U?09`9Hz`Xo-I!P=9XfIrr*MQ}y=$!qgv?_J38^bNb4kM&_OVg^_=Eu-qG5U(fw0KMgH){C8pazq~51rN97hf#20-7=aK0)N|UM H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/test/.gitignore b/test/.gitignore index 2bead7e..e4532bf 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -115,4 +115,9 @@ app.*.symbols !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock \ No newline at end of file +!/dev/ci/**/Gemfile.lock + +# Allow gradlew files for test app so it can be run after cloning +!app/android/gradlew +!app/android/gradlew.bat +!app/android/gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/test/app/.gitignore b/test/app/.gitignore index 2bead7e..30bb16e 100644 --- a/test/app/.gitignore +++ b/test/app/.gitignore @@ -1,14 +1,16 @@ # Miscellaneous *.class -*.lock *.log *.pyc *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ +migrate_working_dir/ # IntelliJ related *.iml @@ -16,103 +18,28 @@ *.iws .idea/ -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/ - -# Flutter repo-specific -/bin/cache/ -/bin/internal/bootstrap.bat -/bin/internal/bootstrap.sh -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/devicelab/ABresults*.json -/dev/docs/doc/ -/dev/docs/flutter.docs.zip -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/dev/integration_tests/**/xcuserdata -/dev/integration_tests/**/Pods -/packages/flutter/coverage/ -version -analysis_benchmark.json - -# packages file containing multi-root paths -.packages.generated +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ -.flutter-plugins .flutter-plugins-dependencies -**/generated_plugin_registrant.dart -.packages .pub-cache/ .pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds +/build/ +/coverage/ -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/key.properties -*.jks - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/.last_build_id -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/ephemeral -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* - -# macOS -**/macos/Flutter/GeneratedPluginRegistrant.swift - -# Coverage -coverage/ - -# Symbols +# Symbolication related app.*.symbols -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock \ No newline at end of file +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release \ No newline at end of file diff --git a/test/app/android/.gitignore b/test/app/android/.gitignore index 0a741cb..be82512 100644 --- a/test/app/android/.gitignore +++ b/test/app/android/.gitignore @@ -1,11 +1,11 @@ -gradle-wrapper.jar /.gradle /captures/ -/gradlew -/gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +# See https://flutter.dev/to/reference-keystore key.properties +**/*.keystore +**/*.jks diff --git a/test/app/android/gradle/wrapper/gradle-wrapper.jar b/test/app/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 GIT binary patch literal 53636 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ⅅ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 H-+%o-(+5aQ literal 0 HcmV?d00001 diff --git a/test/app/android/gradle/wrapper/gradle-wrapper.properties b/test/app/android/gradle/wrapper/gradle-wrapper.properties index efdcc4a..ac3b479 100644 --- a/test/app/android/gradle/wrapper/gradle-wrapper.properties +++ b/test/app/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/test/app/android/gradlew b/test/app/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/test/app/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/test/app/android/gradlew.bat b/test/app/android/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/test/app/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/test/app/ios/.gitignore b/test/app/ios/.gitignore index e96ef60..a2c66e5 100644 --- a/test/app/ios/.gitignore +++ b/test/app/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -11,6 +12,7 @@ Icon? **/Pods/ **/.symlinks/ +Podfile.lock profile xcuserdata **/.generated/ @@ -18,6 +20,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ From 481cf17a5d9d2f90c6b0acba49e6c011c8c6202b Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 16:10:01 +0200 Subject: [PATCH 09/17] refac: centralize .gitignores into the root one --- .gitignore | 114 +++++++++++++++++++++++---------- android/.gitignore | 8 --- example/.gitignore | 45 ------------- example/android/.gitignore | 11 ---- example/ios/.gitignore | 35 ---------- ios/.gitignore | 31 --------- test/.gitignore | 123 ------------------------------------ test/android/.gitignore | 8 --- test/app/.gitignore | 45 ------------- test/app/android/.gitignore | 11 ---- test/app/ios/.gitignore | 35 ---------- test/ios/.gitignore | 36 ----------- 12 files changed, 79 insertions(+), 423 deletions(-) delete mode 100644 android/.gitignore delete mode 100644 example/.gitignore delete mode 100644 example/android/.gitignore delete mode 100644 example/ios/.gitignore delete mode 100644 ios/.gitignore delete mode 100644 test/.gitignore delete mode 100644 test/android/.gitignore delete mode 100644 test/app/.gitignore delete mode 100644 test/app/android/.gitignore delete mode 100644 test/app/ios/.gitignore delete mode 100644 test/ios/.gitignore diff --git a/.gitignore b/.gitignore index 5f3da6b..deb9eb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,13 @@ -# Miscellaneous +# ============================================================================= +# ADJUST FLUTTER SDK - COMPREHENSIVE .GITIGNORE +# ============================================================================= +# This file handles all ignore patterns for the entire project. +# Organized by category for easy maintenance. + +# ============================================================================= +# SYSTEM & EDITOR FILES +# ============================================================================= +# miscellaneous system files *.class *.lock *.log @@ -6,68 +15,70 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ +migrate_working_dir/ -# IntelliJ related +# intellij related *.iml *.ipr *.iws .idea/ -# Visual Studio Code related +# visual studio code related .classpath .project .settings/ .vscode/ -# Flutter repo-specific -/bin/cache/ -/bin/internal/bootstrap.bat -/bin/internal/bootstrap.sh -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/devicelab/ABresults*.json -/dev/docs/doc/ -/dev/docs/flutter.docs.zip -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/dev/integration_tests/**/xcuserdata -/dev/integration_tests/**/Pods -/packages/flutter/coverage/ -analysis_benchmark.json - -# packages file containing multi-root paths -.packages.generated - -# Flutter/Dart/Pub related +# ============================================================================= +# FLUTTER & DART +# ============================================================================= +# flutter/dart/pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies **/generated_plugin_registrant.dart .packages +.packages.generated .pub-cache/ .pub/ build/ +coverage/ flutter_*.png linked_*.ds unlinked.ds unlinked_spec.ds -# Android related -**/android/**/gradle-wrapper.jar +# ============================================================================= +# ANDROID BUILD SYSTEM +# ============================================================================= +# android build artifacts **/android/.gradle **/android/captures/ **/android/local.properties **/android/**/GeneratedPluginRegistrant.java **/android/key.properties +**/android/app/debug +**/android/app/profile +**/android/app/release *.jks +*.keystore -# iOS/XCode related +# android studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# ============================================================================= +# IOS BUILD SYSTEM +# ============================================================================= +# ios/xcode related **/ios/**/*.mode1v3 **/ios/**/*.mode2v3 **/ios/**/*.moved-aside @@ -85,7 +96,6 @@ unlinked_spec.ds **/ios/**/profile **/ios/**/xcuserdata **/ios/.generated/ -**/ios/Flutter/.last_build_id **/ios/Flutter/App.framework **/ios/Flutter/Flutter.framework **/ios/Flutter/Flutter.podspec @@ -97,25 +107,59 @@ unlinked_spec.ds **/ios/Flutter/flutter_export_environment.sh **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* +**/ios/**/dgph -# macOS +# macos **/macos/Flutter/GeneratedPluginRegistrant.swift -# Coverage -coverage/ +# ============================================================================= +# FLUTTER SDK SPECIFIC +# ============================================================================= +# flutter repo-specific (for SDK development) +/bin/cache/ +/bin/internal/bootstrap.bat +/bin/internal/bootstrap.sh +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/devicelab/ABresults*.json +/dev/docs/doc/ +/dev/docs/flutter.docs.zip +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/dev/integration_tests/**/xcuserdata +/dev/integration_tests/**/Pods +/packages/flutter/coverage/ +version +analysis_benchmark.json -# Symbols +# ============================================================================= +# BUILD ARTIFACTS & SYMBOLS +# ============================================================================= +# symbolication related app.*.symbols -# Exceptions to above rules. +# obfuscation related +app.*.map.json + +# ============================================================================= +# EXCEPTIONS - FILES THAT MUST BE TRACKED +# ============================================================================= +# these files are essential for building the apps after cloning + +# allow default ios files !**/ios/**/default.mode1v3 !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 + +# allow essential flutter tools test data !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock -# Allow gradlew files for example and test apps so they can be run after cloning +# allow gradle build system files for example and test apps +# these are essential for building without local gradle installation !example/android/gradlew !example/android/gradlew.bat !example/android/gradle/wrapper/gradle-wrapper.jar diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index c6cbe56..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 3820a95..0000000 --- a/example/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.build/ -.buildlog/ -.history -.svn/ -.swiftpm/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins-dependencies -.pub-cache/ -.pub/ -/build/ -/coverage/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/example/android/.gitignore b/example/android/.gitignore deleted file mode 100644 index be82512..0000000 --- a/example/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -/.gradle -/captures/ -/local.properties -GeneratedPluginRegistrant.java -.cxx/ - -# Remember to never publicly share your keystore. -# See https://flutter.dev/to/reference-keystore -key.properties -**/*.keystore -**/*.jks diff --git a/example/ios/.gitignore b/example/ios/.gitignore deleted file mode 100644 index a2c66e5..0000000 --- a/example/ios/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -Podfile.lock -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 956c87f..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index e4532bf..0000000 --- a/test/.gitignore +++ /dev/null @@ -1,123 +0,0 @@ -# Miscellaneous -*.class -*.lock -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/ - -# Flutter repo-specific -/bin/cache/ -/bin/internal/bootstrap.bat -/bin/internal/bootstrap.sh -/bin/mingit/ -/dev/benchmarks/mega_gallery/ -/dev/bots/.recipe_deps -/dev/bots/android_tools/ -/dev/devicelab/ABresults*.json -/dev/docs/doc/ -/dev/docs/flutter.docs.zip -/dev/docs/lib/ -/dev/docs/pubspec.yaml -/dev/integration_tests/**/xcuserdata -/dev/integration_tests/**/Pods -/packages/flutter/coverage/ -version -analysis_benchmark.json - -# packages file containing multi-root paths -.packages.generated - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -**/generated_plugin_registrant.dart -.packages -.pub-cache/ -.pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds - -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/key.properties -*.jks - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/.last_build_id -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/ephemeral -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* - -# macOS -**/macos/Flutter/GeneratedPluginRegistrant.swift - -# Coverage -coverage/ - -# Symbols -app.*.symbols - -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -!/dev/ci/**/Gemfile.lock - -# Allow gradlew files for test app so it can be run after cloning -!app/android/gradlew -!app/android/gradlew.bat -!app/android/gradle/wrapper/gradle-wrapper.jar \ No newline at end of file diff --git a/test/android/.gitignore b/test/android/.gitignore deleted file mode 100644 index c6cbe56..0000000 --- a/test/android/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures diff --git a/test/app/.gitignore b/test/app/.gitignore deleted file mode 100644 index 30bb16e..0000000 --- a/test/app/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.build/ -.buildlog/ -.history -.svn/ -.swiftpm/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins-dependencies -.pub-cache/ -.pub/ -/build/ -/coverage/ - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release \ No newline at end of file diff --git a/test/app/android/.gitignore b/test/app/android/.gitignore deleted file mode 100644 index be82512..0000000 --- a/test/app/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -/.gradle -/captures/ -/local.properties -GeneratedPluginRegistrant.java -.cxx/ - -# Remember to never publicly share your keystore. -# See https://flutter.dev/to/reference-keystore -key.properties -**/*.keystore -**/*.jks diff --git a/test/app/ios/.gitignore b/test/app/ios/.gitignore deleted file mode 100644 index a2c66e5..0000000 --- a/test/app/ios/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -Podfile.lock -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/test/ios/.gitignore b/test/ios/.gitignore deleted file mode 100644 index 710ec6c..0000000 --- a/test/ios/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig From 4bdb6e2f8ac0f1c7419150824d33434ee793bbc8 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 16:14:42 +0200 Subject: [PATCH 10/17] refac: update .gitignore --- .gitignore | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index deb9eb2..6e6a1cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,12 @@ -# ============================================================================= +# ============================================= # ADJUST FLUTTER SDK - COMPREHENSIVE .GITIGNORE -# ============================================================================= +# ============================================= # This file handles all ignore patterns for the entire project. # Organized by category for easy maintenance. -# ============================================================================= +# ===================== # SYSTEM & EDITOR FILES -# ============================================================================= +# ===================== # miscellaneous system files *.class *.lock @@ -34,9 +34,9 @@ migrate_working_dir/ .settings/ .vscode/ -# ============================================================================= +# ============== # FLUTTER & DART -# ============================================================================= +# ============== # flutter/dart/pub related **/doc/api/ **/ios/Flutter/.last_build_id @@ -55,9 +55,9 @@ linked_*.ds unlinked.ds unlinked_spec.ds -# ============================================================================= +# ==================== # ANDROID BUILD SYSTEM -# ============================================================================= +# ==================== # android build artifacts **/android/.gradle **/android/captures/ @@ -75,9 +75,9 @@ unlinked_spec.ds /android/app/profile /android/app/release -# ============================================================================= +# ================= # IOS BUILD SYSTEM -# ============================================================================= +# ================= # ios/xcode related **/ios/**/*.mode1v3 **/ios/**/*.mode2v3 @@ -112,9 +112,9 @@ unlinked_spec.ds # macos **/macos/Flutter/GeneratedPluginRegistrant.swift -# ============================================================================= +# ==================== # FLUTTER SDK SPECIFIC -# ============================================================================= +# ==================== # flutter repo-specific (for SDK development) /bin/cache/ /bin/internal/bootstrap.bat @@ -134,18 +134,18 @@ unlinked_spec.ds version analysis_benchmark.json -# ============================================================================= +# ========================= # BUILD ARTIFACTS & SYMBOLS -# ============================================================================= +# ========================= # symbolication related app.*.symbols # obfuscation related app.*.map.json -# ============================================================================= +# ======================================= # EXCEPTIONS - FILES THAT MUST BE TRACKED -# ============================================================================= +# ======================================= # these files are essential for building the apps after cloning # allow default ios files From bc20ad87c2da3c3ae08b85612157a1575fd4c507 Mon Sep 17 00:00:00 2001 From: uerceg Date: Fri, 27 Jun 2025 16:23:44 +0200 Subject: [PATCH 11/17] docs: update changelog --- CHANGELOG.md | 306 ++++++++++++++++++++++++--------------------------- 1 file changed, 143 insertions(+), 163 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a8f96..5e46702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### Version 5.4.0 (13th June 2025) + #### Added - Added support for configuring store information via the `AdjustStoreInfo` object. You can now specify the store name and store app ID by assigning the `storeInfo` member of your `AdjustConfig` instance. This enables the SDK to record the intended app store source during initialization. For more details about this feature, refer to the [official documentation](https://dev.adjust.com/en/sdk/flutter/setup/store-type). - Added ability to initialize the SDK for the first session in delayed mode. You can start the SDK in the delayed mode by setting the `isFirstSessionDelayEnabled` member on your `AdjustConfig` instance to `true`. To end the delay, make sure to call `endFirstSessionDelay` method of `Adjust` instance. For more details about this feature, refer to the [official documentation](https://dev.adjust.com/en/sdk/flutter/features/first-session-delay). @@ -6,79 +7,87 @@ - Added ability to disable SDK's interaction with `AppTrackingTransparency.framework` API. You can disable it by setting the `isAppTrackingTransparencyUsageEnabled` member on your `AdjustConfig` instance to `false`. For more details about this feature, refer to the [official documentation](https://dev.adjust.com/en/sdk/flutter/features/att#disable-att-framework). #### Native SDKs -- [iOS@v5.4.0][ios_sdk_v5.4.0] -- [Android@v5.4.0][android_sdk_v5.4.0] +- **iOS:** [v5.4.0](https://github.com/adjust/ios_sdk/tree/v5.4.0) +- **Android:** [v5.4.0](https://github.com/adjust/android_sdk/tree/v5.4.0) --- ### Version 5.1.1 (March 5th 2025) + #### Fixed -- Fixed crashes happening in cases where native iOS `jsonResponse` is `nil` (https://github.com/adjust/flutter_sdk/pull/160). +- Fixed crashes happening in cases where native iOS `jsonResponse` is `nil` ([#160](https://github.com/adjust/flutter_sdk/pull/160)). #### Native SDKs -- [iOS@v5.1.1][ios_sdk_v5.1.1] -- [Android@v5.1.0][android_sdk_v5.1.0] +- **iOS:** [v5.1.1](https://github.com/adjust/ios_sdk/tree/v5.1.1) +- **Android:** [v5.1.0](https://github.com/adjust/android_sdk/tree/v5.1.0) --- ### Version 5.1.0 (25th February 2025) + #### Added - Added `jsonResponse` field (JSON string) to `AdjustAttribution` where every key-value pair sent by the backend as part of the attribution response can be found. #### Native SDKs -- [iOS@v5.1.1][ios_sdk_v5.1.1] -- [Android@v5.1.0][android_sdk_v5.1.0] +- **iOS:** [v5.1.1](https://github.com/adjust/ios_sdk/tree/v5.1.1) +- **Android:** [v5.1.0](https://github.com/adjust/android_sdk/tree/v5.1.0) --- ### Version 5.0.4 (6th February 2025) + #### Added - Added sending of the additional parameter to improve troubleshooting of `SKAdNetwork` related issues. #### Fixed - Fixed occasional occurrences in which ATT waiting interval timer was not being started. -- Fixed occasional NPE occurrences when app was entering background (https://github.com/adjust/android_sdk/issues/630). -- Fixed occasional NPE occurrences when `updateSkanConversionValue` returns `null` as error (https://github.com/adjust/flutter_sdk/issues/156). +- Fixed occasional NPE occurrences when app was entering background ([android_sdk#630](https://github.com/adjust/android_sdk/issues/630)). +- Fixed occasional NPE occurrences when `updateSkanConversionValue` returns `null` as error ([#156](https://github.com/adjust/flutter_sdk/issues/156)). #### Native SDKs -- [iOS@v5.1.0][ios_sdk_v5.1.0] -- [Android@v5.1.0][android_sdk_v5.1.0] +- **iOS:** [v5.1.0](https://github.com/adjust/ios_sdk/tree/v5.1.0) +- **Android:** [v5.1.0](https://github.com/adjust/android_sdk/tree/v5.1.0) --- ### Version 5.0.3 (6th December 2024) + #### Changed - Switched to native Android SDK version that depends on a specific version of the signature library. #### Native SDKs -- [iOS@v5.0.1][ios_sdk_v5.0.1] -- [Android@v5.0.2][android_sdk_v5.0.2] +- **iOS:** [v5.0.1](https://github.com/adjust/ios_sdk/tree/v5.0.1) +- **Android:** [v5.0.2](https://github.com/adjust/android_sdk/tree/v5.0.2) --- ### Version 5.0.2 (23rd September 2024) + #### Fixed - Fixed `Adjust.modulemap not found` error in certain CocoaPods integration cases. - Fixed occasional ANRs while reading install referrer from Shared Preferences during the SDK initialization. #### Native SDKs -- [iOS@v5.0.1][ios_sdk_v5.0.1] -- [Android@v5.0.1][android_sdk_v5.0.1] +- **iOS:** [v5.0.1](https://github.com/adjust/ios_sdk/tree/v5.0.1) +- **Android:** [v5.0.1](https://github.com/adjust/android_sdk/tree/v5.0.1) --- ### Version 5.0.1 (4th September 2024) + #### Fixed - Added missing `Adjust.getLastDeeplink` implementation on iOS platform. #### Native SDKs -- [iOS@v5.0.0][ios_sdk_v5.0.0] -- [Android@v5.0.0][android_sdk_v5.0.0] +- **iOS:** [v5.0.0](https://github.com/adjust/ios_sdk/tree/v5.0.0) +- **Android:** [v5.0.0](https://github.com/adjust/android_sdk/tree/v5.0.0) --- ### Version 5.0.0 (30th August 2024) +**Major Release** + We're excited to release our major new SDK version (v5). Among many internal improvements, our spoofing protection solution is now included out of the box, reinforcing our commitment to accurate, actionable, and fraud-free data. To try out SDK v5 in your app, you can follow our new v4 to v5 [migration guide](https://dev.adjust.com/en/sdk/flutter/migration/v4-to-v5). @@ -88,71 +97,77 @@ If you are a current Adjust client and have questions about SDK v5, please email In case you were using beta version of the SDK v5, please switch to the official v5 release. #### Native SDKs -- [iOS@v5.0.0][ios_sdk_v5.0.0] -- [Android@v5.0.0][android_sdk_v5.0.0] +- **iOS:** [v5.0.0](https://github.com/adjust/ios_sdk/tree/v5.0.0) +- **Android:** [v5.0.0](https://github.com/adjust/android_sdk/tree/v5.0.0) --- ### Version 4.38.2 (10th July 2024) + #### Fixed - Fixed occasional crashes when processing resolved deep links. #### Native SDKs -- [iOS@v4.38.4][ios_sdk_v4.38.4] -- [Android@v4.38.5][android_sdk_v4.38.5] +- **iOS:** [v4.38.4](https://github.com/adjust/ios_sdk/tree/v4.38.4) +- **Android:** [v4.38.5](https://github.com/adjust/android_sdk/tree/v4.38.5) --- ### Version 4.38.1 (30th April 2024) + #### Added - Added sending of the additional iOS SDK observability parameters for debugging purposes. #### Fixed -- Removed nullability type specifier warnings (https://github.com/adjust/ios_sdk/issues/703). +- Removed nullability type specifier warnings ([ios_sdk#703](https://github.com/adjust/ios_sdk/issues/703)). #### Native SDKs -- [iOS@v4.38.2][ios_sdk_v4.38.2] -- [Android@v4.38.3][android_sdk_v4.38.3] +- **iOS:** [v4.38.2](https://github.com/adjust/ios_sdk/tree/v4.38.2) +- **Android:** [v4.38.3](https://github.com/adjust/android_sdk/tree/v4.38.3) --- ### Version 4.38.0 (28th March 2024) + #### Added - Added iOS Privacy Manifest for the Adjust SDK. - Added new domains and corresponding payload restrictions for the Adjust SDK to direct the iOS traffic to: - - https://consent.adjust.com - for consented users - - https://analytics.adjust.com - for non-consented users + - `https://consent.adjust.com` - for consented users + - `https://analytics.adjust.com` - for non-consented users #### Native SDKs -- [iOS@v4.38.0][ios_sdk_v4.38.0] -- [Android@v4.38.3][android_sdk_v4.38.3] +- **iOS:** [v4.38.0](https://github.com/adjust/ios_sdk/tree/v4.38.0) +- **Android:** [v4.38.3](https://github.com/adjust/android_sdk/tree/v4.38.3) --- ### Version 4.37.1 (21st February 2024) + #### Added - Added support for `TradPlus` ad revenue tracking. #### Fixed -- Fixed return type mismatch between native Android and Dart implementation of iOS specific methods (https://github.com/adjust/flutter_sdk/issues/122). +- Fixed return type mismatch between native Android and Dart implementation of iOS specific methods ([#122](https://github.com/adjust/flutter_sdk/issues/122)). #### Native SDKs -- [iOS@v4.37.1][ios_sdk_v4.37.1] -- [Android@v4.38.1][android_sdk_v4.38.1] +- **iOS:** [v4.37.1](https://github.com/adjust/ios_sdk/tree/v4.37.1) +- **Android:** [v4.38.1](https://github.com/adjust/android_sdk/tree/v4.38.1) --- ### Version 4.37.0 (23rd January 2024) + #### Added - Added ability to process shortened deep links and provide the unshortened link back as a response. You can achieve this by invoking `processDeeplink` method of the `Adjust` instance. #### Native SDKs -- [iOS@v4.37.0][ios_sdk_v4.37.0] -- [Android@v4.38.0][android_sdk_v4.38.0] +- **iOS:** [v4.37.0](https://github.com/adjust/ios_sdk/tree/v4.37.0) +- **Android:** [v4.38.0](https://github.com/adjust/android_sdk/tree/v4.38.0) --- ### Version 4.36.0 (27th November 2023) + #### Added - Added getter for obtaining IDFV value of the iOS device. - Added support for Meta install referrer. @@ -162,76 +177,83 @@ In case you were using beta version of the SDK v5, please switch to the official - Added `readDeviceInfoOnceEnabled` member to `AdjustConfig` to indicate if Android device info should be read only once. #### Native SDKs -- [iOS@v4.36.0][ios_sdk_v4.36.0] -- [Android@v4.37.0][android_sdk_v4.37.0] +- **iOS:** [v4.36.0](https://github.com/adjust/ios_sdk/tree/v4.36.0) +- **Android:** [v4.37.0](https://github.com/adjust/android_sdk/tree/v4.37.0) --- ### Version 4.35.2 (9th October 2023) + #### Added - Added sending of `event_callback_id` parameter (if set) with the event payload. #### Native SDKs -- [iOS@v4.35.2][ios_sdk_v4.35.2] -- [Android@v4.35.1][android_sdk_v4.35.1] +- **iOS:** [v4.35.2](https://github.com/adjust/ios_sdk/tree/v4.35.2) +- **Android:** [v4.35.1](https://github.com/adjust/android_sdk/tree/v4.35.1) --- ### Version 4.35.1 (2nd October 2023) + #### Fixed - Fixed issue with signing iOS requests post ATT delay timer expiry. #### Native SDKs -- [iOS@v4.35.1][ios_sdk_v4.35.1] -- [Android@v4.35.0][android_sdk_v4.35.0] +- **iOS:** [v4.35.1](https://github.com/adjust/ios_sdk/tree/v4.35.1) +- **Android:** [v4.35.0](https://github.com/adjust/android_sdk/tree/v4.35.0) --- ### Version 4.35.0 (27th September 2023) + #### Added - Added support for SigV3 library. Update authorization header building logic to use `adj_signing_id`. - Added ability to indicate if only final Android attribution is needed in attribution callback (by default attribution callback return intermediate attribution as well before final attribution if not enabled with this setter method) by setting the `androidFinalAttributionEnabled` member of the `AdjustConfig` instance. #### Native SDKs -- [iOS@v4.35.0][ios_sdk_v4.35.0] -- [Android@v4.35.0][android_sdk_v4.35.0] +- **iOS:** [v4.35.0](https://github.com/adjust/ios_sdk/tree/v4.35.0) +- **Android:** [v4.35.0](https://github.com/adjust/android_sdk/tree/v4.35.0) --- ### Version 4.34.0 (6th September 2023) + #### Added - Added support for Android apps using Gradle 8.0 or later. - Added ability to delay SDK start on iOS platform in order to wait for an answer to the ATT dialog. You can set the number of seconds to wait (capped internally to 120) by setting the `attConsentWaitingInterval` member of the `AdjustConfig` instance. - Added support for purchase verification. In case you are using this feature, you can now use it by calling `verifyAppStorePurchase` (for iOS) and `verifyPlayStorePurchase` (for Android) methods of the `Adjust` instance. #### Native SDKs -- [iOS@v4.34.2][ios_sdk_v4.34.2] -- [Android@v4.34.0][android_sdk_v4.34.0] +- **iOS:** [v4.34.2](https://github.com/adjust/ios_sdk/tree/v4.34.2) +- **Android:** [v4.34.0](https://github.com/adjust/android_sdk/tree/v4.34.0) --- ### Version 4.33.1 (16th February 2023) + #### Fixed - Skipped invocation of SKAN 4.0 style callback in case SKAN 4.0 API was not invoked (https://github.com/adjust/flutter_sdk/issues/104). #### Native SDKs -- [iOS@v4.33.4][ios_sdk_v4.33.4] -- [Android@v4.33.3][android_sdk_v4.33.3] +- **iOS:** [v4.33.4](https://github.com/adjust/ios_sdk/tree/v4.33.4) +- **Android:** [v4.33.3](https://github.com/adjust/android_sdk/tree/v4.33.3) --- ### Version 4.33.0 (9th December 2022) + #### Added - Added support for SKAN 4.0. - Added support for setting a new China URL Strategy. You can choose this setting by setting `urlStrategy` member of `AdjustConfig` instance to `AdjustConfig.UrlStrategyCn` value. #### Native SDKs -- [iOS@v4.33.2][ios_sdk_v4.33.2] -- [Android@v4.33.2][android_sdk_v4.33.2] +- **iOS:** [v4.33.2](https://github.com/adjust/ios_sdk/tree/v4.33.2) +- **Android:** [v4.33.2](https://github.com/adjust/android_sdk/tree/v4.33.2) --- ### Version 4.32.0 (7th October 2022) + #### Added - Added partner sharing settings to the third party sharing feature. - Added `getLastDeeplink` getter to `Adjust` API to be able to get last tracked deep link by the SDK for iOS platform. @@ -240,23 +262,25 @@ In case you were using beta version of the SDK v5, please switch to the official - Switched to adding permission `com.google.android.gms.permission.AD_ID` in the Android app's mainfest by default. #### Native SDKs -- [iOS@v4.32.1][ios_sdk_v4.32.1] -- [Android@v4.32.0][android_sdk_v4.32.0] +- **iOS:** [v4.32.1](https://github.com/adjust/ios_sdk/tree/v4.32.1) +- **Android:** [v4.32.0](https://github.com/adjust/android_sdk/tree/v4.32.0) --- ### Version 4.31.0 (3rd August 2022) + #### Added - Added support for `LinkMe` feature. - Added support to get Facebook install referrer information in attribution callback. #### Native SDKs -- [iOS@v4.31.0][ios_sdk_v4.31.0] -- [Android@v4.31.0][android_sdk_v4.31.0] +- **iOS:** [v4.31.0](https://github.com/adjust/ios_sdk/tree/v4.31.0) +- **Android:** [v4.31.0](https://github.com/adjust/android_sdk/tree/v4.31.0) --- ### Version 4.30.0 (9th June 2022) + #### Added - Added ability to mark your app as COPPA compliant. You can enable this setting by setting `coppaCompliantEnabled` member of `AdjustConfig` instance to `true`. - Added ability to mark your Android app as app for the kids in accordance to Google Play Families policies. You can enable this setting by setting `playStoreKidsAppEnabled` member of `AdjustConfig` instance to `true`. @@ -268,12 +292,13 @@ In case you were using beta version of the SDK v5, please switch to the official - Changed responses which is being returned when iOS specific API is being called on Android platform (https://github.com/adjust/flutter_sdk/issues/79 and https://github.com/adjust/flutter_sdk/issues/80). #### Native SDKs -- [iOS@v4.30.0][ios_sdk_v4.30.0] -- [Android@v4.30.1][android_sdk_v4.30.1] +- **iOS:** [v4.30.0](https://github.com/adjust/ios_sdk/tree/v4.30.0) +- **Android:** [v4.30.1](https://github.com/adjust/android_sdk/tree/v4.30.1) --- ### Version 4.29.2 (18th February 2022) + #### Added - Added support for `Unity` ad revenue tracking. - Added support for `Helium Chartboost` ad revenue tracking. @@ -283,12 +308,13 @@ In case you were using beta version of the SDK v5, please switch to the official - Migrated from `jcenter` to `mavenCentral` repository (https://github.com/adjust/flutter_sdk/pull/72). #### Native SDKs -- [iOS@v4.29.7][ios_sdk_v4.29.7] -- [Android@v4.29.1][android_sdk_v4.29.1] +- **iOS:** [v4.29.7](https://github.com/adjust/ios_sdk/tree/v4.29.7) +- **Android:** [v4.29.1](https://github.com/adjust/android_sdk/tree/v4.29.1) --- ### Version 4.29.1 (23rd September 2021) + #### Added - Added support for `Admost` ad revenue source. @@ -296,49 +322,50 @@ In case you were using beta version of the SDK v5, please switch to the official - Fixed compile time errors with Xcode 13. #### Native SDKs -- [iOS@v4.29.6][ios_sdk_v4.29.6] -- [Android@v4.28.5][android_sdk_v4.28.5] +- **iOS:** [v4.29.6](https://github.com/adjust/ios_sdk/tree/v4.29.6) +- **Android:** [v4.28.5](https://github.com/adjust/android_sdk/tree/v4.28.5) --- ### Version 4.29.0 (11th June 2021) + #### Added - Added support for null safety (thanks to @blaueeiner). - [beta] Added data residency feature. You can choose this setting by setting `urlStrategy` member of `AdjustConfig` instance to `AdjustConfig.DataResidencyEU` (for EU data residency region), `AdjustConfig.DataResidencyTR` (for TR data residency region) or `AdjustConfig.DataResidencyUS` value (for US data residency region). -- Added `trackAdRevenueNew` method to `Adjust` interface to allow tracking of ad revenue by passing `AdjustAdRevenue` object as parameter. +- Added support for `Topon` ad revenue tracking. +- Added support for `IronSource` ad revenue tracking. - Added support for `AppLovin MAX` ad revenue tracking. -- Added `conversionValueUpdatedCallback` member to `AdjustConfig` which can be used to set a callback which will get information when Adjust SDK updates conversion value for the user. - Added preinstall tracking with usage of system installer receiver on Android platform. #### Fixed - Fixed attribution value comparison logic which might cause same attribution value to be delivered into attribution callback on iOS platform. #### Native SDKs -- [iOS@v4.29.2][ios_sdk_v4.29.2] -- [Android@v4.28.1][android_sdk_v4.28.1] +- **iOS:** [v4.29.2](https://github.com/adjust/ios_sdk/tree/v4.29.2) +- **Android:** [v4.28.1](https://github.com/adjust/android_sdk/tree/v4.28.1) --- ### Version 4.28.0 (2nd April 2021) + #### Changed - Removed native iOS legacy code. #### Native SDKs -- [iOS@v4.28.0][ios_sdk_v4.28.0] -- [Android@v4.27.0][android_sdk_v4.27.0] +- **iOS:** [v4.28.0](https://github.com/adjust/ios_sdk/tree/v4.28.0) +- **Android:** [v4.27.0](https://github.com/adjust/android_sdk/tree/v4.27.0) --- ### Version 4.26.0 (23rd February 2021) + #### Added - Added possibility to get cost data information in attribution callback. - Added `needsCost` member to `AdjustConfig` to indicate if cost data is needed in attribution callback (by default cost data will not be part of attribution callback if not enabled with this setter method). -- Added `preinstallTrackingEnabled` member to `AdjustConfig` to allow enabling of preinstall tracking (this feature is OFF by default). -- Added support for Apple Search Ads attribution with usage of `AdServices.framework`. -- Added `allowAdServicesInfoReading` member to `AdjustConfig` to allow option for users to prevent SDK from performing any tasks related to Apple Search Ads attribution with usage of `AdServices.framework`. -- Added wrapper method `updateConversionValue` method to `Adjust` to allow updating SKAdNetwork conversion value via SDK API. -- Added `getAppTrackingAuthorizationStatus` getter to `Adjust` instance to be able to get current iOS app tracking status. -- Added improved measurement consent management and third party sharing mechanism. +- Added `AdjustPlayStoreSubscription` class to be used when tracking Play Store subscriptions. +- Added `AdjustAppStoreSubscription` class to be used when tracking App Store subscriptions. +- Added `trackAppStoreSubscription` method to `Adjust` interface to allow tracking of App Store subscriptions. +- Added `trackPlayStoreSubscription` method to `Adjust` interface to allow tracking of Play Store subscriptions. - Added public constants to be used as sources for ad revenue tracking with `trackAdRevenue` method. #### Changed @@ -348,43 +375,47 @@ In case you were using beta version of the SDK v5, please switch to the official - Fixed occasional NPEs in Android when firing callback methods. #### Native SDKs -- [iOS@v4.26.1][ios_sdk_v4.26.1] -- [Android@v4.26.2][android_sdk_v4.26.2] +- **iOS:** [v4.26.1](https://github.com/adjust/ios_sdk/tree/v4.26.1) +- **Android:** [v4.26.2](https://github.com/adjust/android_sdk/tree/v4.26.2) --- ### Version 4.23.3 (18th December 2020) + #### Added - Added URL strategy constants to `AdjustConfig` for more straight forward feature usage. #### Native SDKs -- [iOS@v4.23.2][ios_sdk_v4.23.2] -- [Android@v4.24.1][android_sdk_v4.24.1] +- **iOS:** [v4.23.2](https://github.com/adjust/ios_sdk/tree/v4.23.2) +- **Android:** [v4.24.1](https://github.com/adjust/android_sdk/tree/v4.24.1) --- ### Version 4.23.2 (11th November 2020) + #### Added - Added [Flutter 1.2 or later](https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects) support for Android projects. #### Native SDKs -- [iOS@v4.23.2][ios_sdk_v4.23.2] -- [Android@v4.24.1][android_sdk_v4.24.1] +- **iOS:** [v4.23.2](https://github.com/adjust/ios_sdk/tree/v4.23.2) +- **Android:** [v4.24.1](https://github.com/adjust/android_sdk/tree/v4.24.1) --- ### Version 4.23.1 (31st August 2020) + #### Fixed - Removed `iosPrefix` from `pubspec.yaml`. - Removed `ADJ` prefix from Flutter iOS class names. #### Native SDKs -- [iOS@v4.23.0][ios_sdk_v4.23.0] -- [Android@v4.24.0][android_sdk_v4.24.0] +- **iOS:** [v4.23.0](https://github.com/adjust/ios_sdk/tree/v4.23.0) +- **Android:** [v4.24.0](https://github.com/adjust/android_sdk/tree/v4.24.0) --- ### Version 4.23.0 (28th August 2020) + #### Added - Added communication with SKAdNetwork framework by default on iOS 14. - Added method `deactivateSKAdNetworkHandling` method to `AdjustConfig` to switch off default communication with SKAdNetwork framework in iOS 14. @@ -394,22 +425,24 @@ In case you were using beta version of the SDK v5, please switch to the official - Added `urlStrategy` member to `AdjustConfig` class to allow selection of URL strategy for specific market. #### Native SDKs -- [iOS@v4.23.0][ios_sdk_v4.23.0] -- [Android@v4.24.0][android_sdk_v4.24.0] +- **iOS:** [v4.23.0](https://github.com/adjust/ios_sdk/tree/v4.23.0) +- **Android:** [v4.24.0](https://github.com/adjust/android_sdk/tree/v4.24.0) --- ### Version 4.22.1 (3rd August 2020) + #### Changed - Changed referencing of native Android dependency from `implementation` to `api`. #### Native SDKs -- [iOS@v4.22.1][ios_sdk_v4.22.1] -- [Android@v4.22.0][android_sdk_v4.22.0] +- **iOS:** [v4.22.1](https://github.com/adjust/ios_sdk/tree/v4.22.1) +- **Android:** [v4.22.0](https://github.com/adjust/android_sdk/tree/v4.22.0) --- ### Version 4.22.0 (10th June 2020) + #### Added - Added subscription tracking feature. - Added support for Huawei App Gallery install referrer. @@ -418,12 +451,13 @@ In case you were using beta version of the SDK v5, please switch to the official - Updated communication flow with `iAd.framework`. #### Native SDKs -- [iOS@v4.22.1][ios_sdk_v4.22.1] -- [Android@v4.22.0][android_sdk_v4.22.0] +- **iOS:** [v4.22.1](https://github.com/adjust/ios_sdk/tree/v4.22.1) +- **Android:** [v4.22.0](https://github.com/adjust/android_sdk/tree/v4.22.0) --- ### Version 4.21.0 (25th March 2021) + #### Added - Added `disableThirdPartySharing` method to `Adjust` interface to allow disabling of data sharing with third parties outside of Adjust ecosystem. - Added support for signature library as a plugin. @@ -432,150 +466,96 @@ In case you were using beta version of the SDK v5, please switch to the official - Added external device ID support. #### Native SDKs -- [iOS@v4.21.0][ios_sdk_v4.21.0] -- [Android@4.21.0][android_sdk_v4.21.0] +- **iOS:** [v4.21.0](https://github.com/adjust/ios_sdk/tree/v4.21.0) +- **Android:** [v4.21.0](https://github.com/adjust/android_sdk/tree/v4.21.0) --- ### Version 4.18.1 (9th October 2019) + #### Fixed - Fixed lack of `getAdid` method implementation in native iOS bridge (thanks to @HenriBeck). #### Native SDKs -- [iOS@v4.18.3][ios_sdk_v4.18.3] -- [Android@v4.18.3][android_sdk_v4.18.3] +- **iOS:** [v4.18.3](https://github.com/adjust/ios_sdk/tree/v4.18.3) +- **Android:** [v4.18.3](https://github.com/adjust/android_sdk/tree/v4.18.3) --- ### Version 4.18.0 (4th July 2019) + #### Added - Added `trackAdRevenue` method to `Adjust` interface to allow tracking of ad revenue. With this release added support for `MoPub` ad revenue tracking. - Added reading of Facebook anonymous ID if available on iOS platform. #### Native SDKs -- [iOS@v4.18.0][ios_sdk_v4.18.0] -- [Android@v4.18.0][android_sdk_v4.18.0] +- **iOS:** [v4.18.0](https://github.com/adjust/ios_sdk/tree/v4.18.0) +- **Android:** [v4.18.0](https://github.com/adjust/android_sdk/tree/v4.18.0) --- ### Version 4.17.1 (5th June 2019) + #### Fixed - Fixed issue when trying to register Android plugin more than once (https://github.com/adjust/flutter_sdk/issues/7). #### Native SDKs -- [iOS@v4.17.3][ios_sdk_v4.17.3] -- [Android@v4.17.0][android_sdk_v4.17.0] +- **iOS:** [v4.17.3](https://github.com/adjust/ios_sdk/tree/v4.17.3) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) --- ### Version 4.17.0 (4th December 2018) + #### Added - Official Flutter SDK release. #### Native SDKs -- [iOS@v4.17.0][ios_sdk_v4.17.0] -- [Android@v4.17.0][android_sdk_v4.17.0] +- **iOS:** [v4.17.0](https://github.com/adjust/ios_sdk/tree/v4.17.0) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) --- ### Version 0.0.4 (4th December 2018) + #### Changed - Changed SDK API to be more Dart friendly. #### Native SDKs -- [iOS@v4.17.0][ios_sdk_v4.17.0] -- [Android@v4.17.0][android_sdk_v4.17.0] +- **iOS:** [v4.17.0](https://github.com/adjust/ios_sdk/tree/v4.17.0) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) --- ### Version 0.0.3 (4th December 2018) + #### Changed - Changed SDK dependency in example app from local path to Flutter plugin repository. #### Native SDKs -- [iOS@v4.17.0][ios_sdk_v4.17.0] -- [Android@v4.17.0][android_sdk_v4.17.0] +- **iOS:** [v4.17.0](https://github.com/adjust/ios_sdk/tree/v4.17.0) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) --- ### Version 0.0.2 (4th December 2018) + #### Added - Added example app to repo. - Added handling of process name for Android platform. #### Native SDKs -- [iOS@v4.17.0][ios_sdk_v4.17.0] -- [Android@v4.17.0][android_sdk_v4.17.0] +- **iOS:** [v4.17.0](https://github.com/adjust/ios_sdk/tree/v4.17.0) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) --- ### Version 0.0.1 (4th December 2018) + #### Added - Test release of Flutter SDK. - Package available at: https://pub.dartlang.org/packages/adjust_sdk #### Native SDKs -- [iOS@v4.17.0][ios_sdk_v4.17.0] -- [Android@v4.17.0][android_sdk_v4.17.0] - -[ios_sdk_v4.17.0]: https://github.com/adjust/ios_sdk/tree/v4.17.0 -[ios_sdk_v4.17.3]: https://github.com/adjust/ios_sdk/tree/v4.17.3 -[ios_sdk_v4.18.0]: https://github.com/adjust/ios_sdk/tree/v4.18.0 -[ios_sdk_v4.18.3]: https://github.com/adjust/ios_sdk/tree/v4.18.3 -[ios_sdk_v4.21.0]: https://github.com/adjust/ios_sdk/tree/v4.21.0 -[ios_sdk_v4.22.1]: https://github.com/adjust/ios_sdk/tree/v4.22.1 -[ios_sdk_v4.23.0]: https://github.com/adjust/ios_sdk/tree/v4.23.0 -[ios_sdk_v4.23.2]: https://github.com/adjust/ios_sdk/tree/v4.23.2 -[ios_sdk_v4.26.1]: https://github.com/adjust/ios_sdk/tree/v4.26.1 -[ios_sdk_v4.28.0]: https://github.com/adjust/ios_sdk/tree/v4.28.0 -[ios_sdk_v4.29.2]: https://github.com/adjust/ios_sdk/tree/v4.29.2 -[ios_sdk_v4.29.6]: https://github.com/adjust/ios_sdk/tree/v4.29.6 -[ios_sdk_v4.29.7]: https://github.com/adjust/ios_sdk/tree/v4.29.7 -[ios_sdk_v4.30.0]: https://github.com/adjust/ios_sdk/tree/v4.30.0 -[ios_sdk_v4.31.0]: https://github.com/adjust/ios_sdk/tree/v4.31.0 -[ios_sdk_v4.32.1]: https://github.com/adjust/ios_sdk/tree/v4.32.1 -[ios_sdk_v4.33.2]: https://github.com/adjust/ios_sdk/tree/v4.33.2 -[ios_sdk_v4.33.4]: https://github.com/adjust/ios_sdk/tree/v4.33.4 -[ios_sdk_v4.34.2]: https://github.com/adjust/ios_sdk/tree/v4.34.2 -[ios_sdk_v4.35.0]: https://github.com/adjust/ios_sdk/tree/v4.35.0 -[ios_sdk_v4.35.1]: https://github.com/adjust/ios_sdk/tree/v4.35.1 -[ios_sdk_v4.35.2]: https://github.com/adjust/ios_sdk/tree/v4.35.2 -[ios_sdk_v4.36.0]: https://github.com/adjust/ios_sdk/tree/v4.36.0 -[ios_sdk_v4.37.0]: https://github.com/adjust/ios_sdk/tree/v4.37.0 -[ios_sdk_v4.37.1]: https://github.com/adjust/ios_sdk/tree/v4.37.1 -[ios_sdk_v4.38.0]: https://github.com/adjust/ios_sdk/tree/v4.38.0 -[ios_sdk_v4.38.2]: https://github.com/adjust/ios_sdk/tree/v4.38.2 -[ios_sdk_v4.38.4]: https://github.com/adjust/ios_sdk/tree/v4.38.4 -[ios_sdk_v5.0.0]: https://github.com/adjust/ios_sdk/tree/v5.0.0 -[ios_sdk_v5.0.1]: https://github.com/adjust/ios_sdk/tree/v5.0.1 -[ios_sdk_v5.1.1]: https://github.com/adjust/ios_sdk/tree/v5.1.1 -[ios_sdk_v5.4.0]: https://github.com/adjust/ios_sdk/tree/v5.4.0 - -[android_sdk_v4.17.0]: https://github.com/adjust/android_sdk/tree/v4.17.0 -[android_sdk_v4.18.0]: https://github.com/adjust/android_sdk/tree/v4.18.0 -[android_sdk_v4.21.0]: https://github.com/adjust/android_sdk/tree/v4.21.0 -[android_sdk_v4.22.0]: https://github.com/adjust/android_sdk/tree/v4.22.0 -[android_sdk_v4.24.0]: https://github.com/adjust/android_sdk/tree/v4.24.0 -[android_sdk_v4.24.1]: https://github.com/adjust/android_sdk/tree/v4.24.1 -[android_sdk_v4.26.2]: https://github.com/adjust/android_sdk/tree/v4.26.2 -[android_sdk_v4.27.0]: https://github.com/adjust/android_sdk/tree/v4.27.0 -[android_sdk_v4.28.1]: https://github.com/adjust/android_sdk/tree/v4.28.1 -[android_sdk_v4.28.5]: https://github.com/adjust/android_sdk/tree/v4.28.5 -[android_sdk_v4.30.1]: https://github.com/adjust/android_sdk/tree/v4.30.1 -[android_sdk_v4.31.0]: https://github.com/adjust/android_sdk/tree/v4.31.0 -[android_sdk_v4.32.0]: https://github.com/adjust/android_sdk/tree/v4.32.0 -[android_sdk_v4.33.2]: https://github.com/adjust/android_sdk/tree/v4.33.2 -[android_sdk_v4.33.3]: https://github.com/adjust/android_sdk/tree/v4.33.3 -[android_sdk_v4.34.0]: https://github.com/adjust/android_sdk/tree/v4.34.0 -[android_sdk_v4.35.0]: https://github.com/adjust/android_sdk/tree/v4.35.0 -[android_sdk_v4.35.1]: https://github.com/adjust/android_sdk/tree/v4.35.1 -[android_sdk_v4.37.0]: https://github.com/adjust/android_sdk/tree/v4.37.0 -[android_sdk_v4.38.0]: https://github.com/adjust/android_sdk/tree/v4.38.0 -[android_sdk_v4.38.1]: https://github.com/adjust/android_sdk/tree/v4.38.1 -[android_sdk_v4.38.3]: https://github.com/adjust/android_sdk/tree/v4.38.3 -[android_sdk_v4.38.5]: https://github.com/adjust/android_sdk/tree/v4.38.5 -[android_sdk_v5.0.0]: https://github.com/adjust/android_sdk/tree/v5.0.0 -[android_sdk_v5.0.1]: https://github.com/adjust/android_sdk/tree/v5.0.1 -[android_sdk_v5.0.2]: https://github.com/adjust/android_sdk/tree/v5.0.2 -[android_sdk_v5.1.0]: https://github.com/adjust/android_sdk/tree/v5.1.0 -[android_sdk_v5.4.0]: https://github.com/adjust/android_sdk/tree/v5.4.0 +- **iOS:** [v4.17.0](https://github.com/adjust/ios_sdk/tree/v4.17.0) +- **Android:** [v4.17.0](https://github.com/adjust/android_sdk/tree/v4.17.0) From 6085a58a2301fd7ada96c14c038fc89979085d1d Mon Sep 17 00:00:00 2001 From: uerceg Date: Mon, 30 Jun 2025 10:18:05 +0200 Subject: [PATCH 12/17] refac: update example app --- example/ios/Runner.xcodeproj/project.pbxproj | 8 ++++---- example/lib/main.dart | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 7875912..5fa1941 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B5B8498EACC1EC5928B2C39 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF876297B32EC6E04F7E818F /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 88F4A3FD2DC44C7129665E88 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -67,6 +68,7 @@ 9DE448292E0E7D7A008D9604 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; 9DE4482B2E0E7DB1008D9604 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = RunnerTests.xctest; path = "/Users/uerceg/GitHub/flutter_sdk_dev/example/ios/build/Debug-iphoneos/Runner.app/PlugIns/RunnerTests.xctest"; sourceTree = ""; }; ACF65A8BFDE5CDDA65DDBFDC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + BF876297B32EC6E04F7E818F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E65025A74749A988F3B85BFF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -76,6 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3B5B8498EACC1EC5928B2C39 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,6 +105,7 @@ 9DE448252E0E7D71008D9604 /* AdServices.framework */, 9DE448232E0E7D6C008D9604 /* AdSupport.framework */, DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */, + BF876297B32EC6E04F7E818F /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -308,14 +312,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; diff --git a/example/lib/main.dart b/example/lib/main.dart index ef3051d..a52f264 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -75,7 +75,7 @@ class _HomePageState extends State with WidgetsBindingObserver { super.dispose(); } - /// initialize the Adjust SDK with comprehensive configuration + /// initialize the Adjust SDK with configuration Future _initializeAdjustSdk() async { setState(() => _isLoading = true); @@ -316,7 +316,7 @@ class _HomePageState extends State with WidgetsBindingObserver { ), const SizedBox(height: 8), Text( - 'Explore the full functionality of the Adjust SDK\nwith this comprehensive example application', + 'Explore the full functionality of the Adjust SDK\nwith this example application', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, @@ -370,7 +370,7 @@ class _HomePageState extends State with WidgetsBindingObserver { const SizedBox(height: 16), _buildActionButton('Get Google AdId', Icons.android, _getGoogleAdId), const SizedBox(height: 12), - _buildActionButton('Get Adjust Identifier', Icons.fingerprint, _getAdjustIdentifier), + _buildActionButton('Get Adjust Identifier', Icons.alternate_email, _getAdjustIdentifier), const SizedBox(height: 12), _buildActionButton('Get IDFA', Icons.phone_iphone, _getIdfa), const SizedBox(height: 12), From 58a80b4c535e2750eec15687e273ef017b3d12cb Mon Sep 17 00:00:00 2001 From: uerceg Date: Mon, 30 Jun 2025 13:18:55 +0200 Subject: [PATCH 13/17] docs: update changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e46702..a2ebdc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +### Version 5.4.1 (1st July 2025) + +#### Added +- Added support for Google On-Device-Measurement. For more details about this feature, refer to the [official documentation](https://dev.adjust.com/en/sdk/flutter/plugins/google-odm). +- Optimized the logic behind the `processAndResolveDeeplink` method to immediately return links that have already been resolved. + +#### Changed +- Updated the Adjust Signature library version to 3.47.0. + +#### Native SDKs +- **iOS:** [v5.4.1](https://github.com/adjust/ios_sdk/tree/v5.4.1) +- **Android:** [v5.4.1](https://github.com/adjust/android_sdk/tree/v5.4.1) + +--- + ### Version 5.4.0 (13th June 2025) #### Added From ebdeeb4759f382e7862846564aed033960126757 Mon Sep 17 00:00:00 2001 From: uerceg Date: Mon, 30 Jun 2025 13:39:53 +0200 Subject: [PATCH 14/17] docs: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2ebdc9..b67521f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ #### Changed - Updated the Adjust Signature library version to 3.47.0. +- Updated look & feel of example and test apps. #### Native SDKs - **iOS:** [v5.4.1](https://github.com/adjust/ios_sdk/tree/v5.4.1) From 3eacf050d76f734e1fe32d3acc7a4c312cdff775 Mon Sep 17 00:00:00 2001 From: uerceg Date: Tue, 1 Jul 2025 12:06:05 +0200 Subject: [PATCH 15/17] chore: update example app --- example/ios/Runner.xcodeproj/project.pbxproj | 5 +++-- example/ios/Runner/Info.plist | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 5fa1941..f8be551 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -62,11 +62,11 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D70DCE02E13E63100BC089B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9DE448232E0E7D6C008D9604 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; 9DE448252E0E7D71008D9604 /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; }; 9DE448272E0E7D76008D9604 /* AppTrackingTransparency.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppTrackingTransparency.framework; path = System/Library/Frameworks/AppTrackingTransparency.framework; sourceTree = SDKROOT; }; 9DE448292E0E7D7A008D9604 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; - 9DE4482B2E0E7DB1008D9604 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = RunnerTests.xctest; path = "/Users/uerceg/GitHub/flutter_sdk_dev/example/ios/build/Debug-iphoneos/Runner.app/PlugIns/RunnerTests.xctest"; sourceTree = ""; }; ACF65A8BFDE5CDDA65DDBFDC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; BF876297B32EC6E04F7E818F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DC62EBC5D8990B3CF54CF3BD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -136,6 +136,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 9D70DCE02E13E63100BC089B /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -187,7 +188,7 @@ ); name = RunnerTests; productName = RunnerTests; - productReference = 9DE4482B2E0E7DB1008D9604 /* RunnerTests.xctest */; + productReference = 9D70DCE02E13E63100BC089B /* RunnerTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 97C146ED1CF9000F007C117D /* Runner */ = { diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 2144dd4..04edb1a 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -24,6 +26,10 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSUserTrackingUsageDescription + This is needed to access IDFA + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,11 +47,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - CADisableMinimumFrameDurationOnPhone - - NSUserTrackingUsageDescription - This is needed to access IDFA - UIApplicationSupportsIndirectInputEvents - From fb35ac7076485ca35e0476a6833afe9ae439748e Mon Sep 17 00:00:00 2001 From: uerceg Date: Tue, 1 Jul 2025 13:46:06 +0200 Subject: [PATCH 16/17] feat: add .pubignore --- .pubignore | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .pubignore diff --git a/.pubignore b/.pubignore new file mode 100644 index 0000000..e39e3d7 --- /dev/null +++ b/.pubignore @@ -0,0 +1,17 @@ +# Exclude test directory from published package +test/ + +# Exclude build artifacts and development files +**/build/ +**/.dart_tool/ +**/Pods/ +**/.pub-cache/ +**/example/build/ +**/example/ios/build/ +**/example/android/build/ +**/example/.dart_tool/ +**/example/ios/Pods/ +**/example/ios/Podfile.lock +**/example/android/local.properties +**/*.iml +**/.DS_Store \ No newline at end of file From bfaa2e391e7627d56a9dcc0a0f80e7ecef49196e Mon Sep 17 00:00:00 2001 From: uerceg Date: Tue, 1 Jul 2025 13:46:28 +0200 Subject: [PATCH 17/17] refac: clean up --- example/pubspec.yaml | 5 ++++ lib/adjust_app_store_purchase.dart | 8 ++---- lib/adjust_app_store_subscription.dart | 12 +++------ lib/adjust_deeplink.dart | 6 +---- lib/adjust_play_store_purchase.dart | 8 ++---- lib/adjust_play_store_subscription.dart | 26 +++++-------------- lib/adjust_purchase_verification_result.dart | 14 +++------- lib/adjust_store_info.dart | 2 -- pubspec.yaml | 2 +- .../com/adjust/test/lib/TestLibPlugin.java | 2 +- test/app/pubspec.yaml | 5 ++++ test/ios/Classes/AdjustCommandExecutor.h | 2 +- test/ios/Classes/AdjustCommandExecutor.m | 2 +- test/ios/Classes/TestLibPlugin.h | 2 +- test/ios/Classes/TestLibPlugin.m | 2 +- test/lib/test_lib.dart | 2 +- 16 files changed, 35 insertions(+), 65 deletions(-) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ce8d8dc..55796b0 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,6 +12,11 @@ dependencies: adjust_sdk: path: ../ +dependency_overrides: + characters: ^1.4.1 + material_color_utilities: ^0.13.0 + meta: ^1.17.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/lib/adjust_app_store_purchase.dart b/lib/adjust_app_store_purchase.dart index 14b0c8b..afde1c0 100644 --- a/lib/adjust_app_store_purchase.dart +++ b/lib/adjust_app_store_purchase.dart @@ -15,12 +15,8 @@ class AdjustAppStorePurchase { Map get toMap { Map purchaseMap = new Map(); - if (_productId != null) { - purchaseMap['productId'] = _productId; - } - if (_transactionId != null) { - purchaseMap['transactionId'] = _transactionId; - } + purchaseMap['productId'] = _productId; + purchaseMap['transactionId'] = _transactionId; return purchaseMap; } diff --git a/lib/adjust_app_store_subscription.dart b/lib/adjust_app_store_subscription.dart index 0500617..66b0e20 100644 --- a/lib/adjust_app_store_subscription.dart +++ b/lib/adjust_app_store_subscription.dart @@ -35,15 +35,9 @@ class AdjustAppStoreSubscription { Map get toMap { Map subscriptionMap = new Map(); - if (_price != null) { - subscriptionMap['price'] = _price; - } - if (_currency != null) { - subscriptionMap['currency'] = _currency; - } - if (_transactionId != null) { - subscriptionMap['transactionId'] = _transactionId; - } + subscriptionMap['price'] = _price; + subscriptionMap['currency'] = _currency; + subscriptionMap['transactionId'] = _transactionId; if (transactionDate != null) { subscriptionMap['transactionDate'] = transactionDate; } diff --git a/lib/adjust_deeplink.dart b/lib/adjust_deeplink.dart index 8c4db04..f0da652 100644 --- a/lib/adjust_deeplink.dart +++ b/lib/adjust_deeplink.dart @@ -6,8 +6,6 @@ // Copyright (c) 2024-Present Adjust GmbH. All rights reserved. // -import 'dart:convert'; - class AdjustDeeplink { String deeplink; String? referrer; @@ -17,9 +15,7 @@ class AdjustDeeplink { Map get toMap { Map deeplinkMap = new Map(); - if (deeplink != null) { - deeplinkMap['deeplink'] = deeplink; - } + deeplinkMap['deeplink'] = deeplink; if (referrer != null) { deeplinkMap['referrer'] = referrer; } diff --git a/lib/adjust_play_store_purchase.dart b/lib/adjust_play_store_purchase.dart index 9a59def..052da78 100644 --- a/lib/adjust_play_store_purchase.dart +++ b/lib/adjust_play_store_purchase.dart @@ -15,12 +15,8 @@ class AdjustPlayStorePurchase { Map get toMap { Map purchaseMap = new Map(); - if (_productId != null) { - purchaseMap['productId'] = _productId; - } - if (_purchaseToken != null) { - purchaseMap['purchaseToken'] = _purchaseToken; - } + purchaseMap['productId'] = _productId; + purchaseMap['purchaseToken'] = _purchaseToken; return purchaseMap; } diff --git a/lib/adjust_play_store_subscription.dart b/lib/adjust_play_store_subscription.dart index 4924039..ad905ff 100644 --- a/lib/adjust_play_store_subscription.dart +++ b/lib/adjust_play_store_subscription.dart @@ -41,26 +41,14 @@ class AdjustPlayStoreSubscription { } Map get toMap { - Map subscriptionMap = new Map(); + Map subscriptionMap = new Map(); - if (_price != null) { - subscriptionMap['price'] = _price; - } - if (_currency != null) { - subscriptionMap['currency'] = _currency; - } - if (_sku != null) { - subscriptionMap['sku'] = _sku; - } - if (_orderId != null) { - subscriptionMap['orderId'] = _orderId; - } - if (_signature != null) { - subscriptionMap['signature'] = _signature; - } - if (_purchaseToken != null) { - subscriptionMap['purchaseToken'] = _purchaseToken; - } + subscriptionMap['price'] = _price; + subscriptionMap['currency'] = _currency; + subscriptionMap['sku'] = _sku; + subscriptionMap['orderId'] = _orderId; + subscriptionMap['signature'] = _signature; + subscriptionMap['purchaseToken'] = _purchaseToken; if (_billingStore != null) { subscriptionMap['billingStore'] = _billingStore; } diff --git a/lib/adjust_purchase_verification_result.dart b/lib/adjust_purchase_verification_result.dart index 41f9b21..265681d 100644 --- a/lib/adjust_purchase_verification_result.dart +++ b/lib/adjust_purchase_verification_result.dart @@ -6,8 +6,6 @@ // Copyright (c) 2020-Present Adjust GmbH. All rights reserved. // -import 'dart:convert'; - class AdjustPurchaseVerificationResult { final num code; final String message; @@ -35,15 +33,9 @@ class AdjustPurchaseVerificationResult { Map get toMap { Map verificationInfoMap = new Map(); - if (code != null) { - verificationInfoMap['code'] = code.toString(); - } - if (message != null) { - verificationInfoMap['message'] = message; - } - if (verificationStatus != null) { - verificationInfoMap['verificationStatus'] = verificationStatus; - } + verificationInfoMap['code'] = code.toString(); + verificationInfoMap['message'] = message; + verificationInfoMap['verificationStatus'] = verificationStatus; return verificationInfoMap; } diff --git a/lib/adjust_store_info.dart b/lib/adjust_store_info.dart index fbcbff2..cdfd680 100644 --- a/lib/adjust_store_info.dart +++ b/lib/adjust_store_info.dart @@ -6,8 +6,6 @@ // Copyright (c) 2025-Present Adjust GmbH. All rights reserved. // -import 'dart:convert'; - class AdjustStoreInfo { String? storeName; String? storeAppId; diff --git a/pubspec.yaml b/pubspec.yaml index 2ccf7bc..b50fca8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/adjust/flutter_sdk version: 5.4.1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: ">=1.10.0" dependencies: diff --git a/test/android/src/main/java/com/adjust/test/lib/TestLibPlugin.java b/test/android/src/main/java/com/adjust/test/lib/TestLibPlugin.java index 2659c9f..26bf193 100644 --- a/test/android/src/main/java/com/adjust/test/lib/TestLibPlugin.java +++ b/test/android/src/main/java/com/adjust/test/lib/TestLibPlugin.java @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 1st October 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // package com.adjust.test.lib; diff --git a/test/app/pubspec.yaml b/test/app/pubspec.yaml index 33ab990..17e003c 100644 --- a/test/app/pubspec.yaml +++ b/test/app/pubspec.yaml @@ -14,6 +14,11 @@ dependencies: test_lib: path: ../ +dependency_overrides: + characters: ^1.4.1 + material_color_utilities: ^0.13.0 + meta: ^1.17.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/test/ios/Classes/AdjustCommandExecutor.h b/test/ios/Classes/AdjustCommandExecutor.h index bda06e4..de35fab 100644 --- a/test/ios/Classes/AdjustCommandExecutor.h +++ b/test/ios/Classes/AdjustCommandExecutor.h @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 1st October 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // #import diff --git a/test/ios/Classes/AdjustCommandExecutor.m b/test/ios/Classes/AdjustCommandExecutor.m index 57d5476..e7904b0 100644 --- a/test/ios/Classes/AdjustCommandExecutor.m +++ b/test/ios/Classes/AdjustCommandExecutor.m @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 1st October 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // #import diff --git a/test/ios/Classes/TestLibPlugin.h b/test/ios/Classes/TestLibPlugin.h index b58429d..5ef6f8d 100644 --- a/test/ios/Classes/TestLibPlugin.h +++ b/test/ios/Classes/TestLibPlugin.h @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 1st October 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // #import diff --git a/test/ios/Classes/TestLibPlugin.m b/test/ios/Classes/TestLibPlugin.m index 3767197..1027a3e 100644 --- a/test/ios/Classes/TestLibPlugin.m +++ b/test/ios/Classes/TestLibPlugin.m @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 1st October 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // #import "TestLibPlugin.h" diff --git a/test/lib/test_lib.dart b/test/lib/test_lib.dart index c76057b..beca10e 100644 --- a/test/lib/test_lib.dart +++ b/test/lib/test_lib.dart @@ -3,7 +3,7 @@ // Adjust SDK // // Created by Srdjan Tubin (@2beens) on 25th April 2018. -// Copyright (c) 2018-2021 Adjust GmbH. All rights reserved. +// Copyright (c) 2018-Present Adjust GmbH. All rights reserved. // import 'package:flutter/services.dart';