From 86bdd9c0324b5c6b60189f1f8b7b697b14512f73 Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Wed, 18 Oct 2023 13:32:22 +0800 Subject: [PATCH 1/5] feat: first commit --- .gitignore | 30 + .metadata | 33 + CHANGELOG.md | 3 + README.md | 23 +- analysis_options.yaml | 4 + android/.gitignore | 9 + android/build.gradle | 68 ++ android/settings.gradle | 1 + android/src/main/AndroidManifest.xml | 3 + .../ClickstreamFlutterPlugin.kt | 35 + .../ClickstreamFlutterPluginTest.kt | 27 + example/.gitignore | 44 ++ example/README.md | 16 + example/analysis_options.yaml | 28 + example/android/.gitignore | 13 + example/android/app/build.gradle | 67 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 33 + .../MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + example/android/build.gradle | 31 + example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + example/android/settings.gradle | 20 + .../plugin_integration_test.dart | 25 + example/ios/.gitignore | 34 + example/ios/Flutter/AppFrameworkInfo.plist | 26 + example/ios/Flutter/Debug.xcconfig | 2 + example/ios/Flutter/Release.xcconfig | 2 + example/ios/Podfile | 44 ++ example/ios/Runner.xcodeproj/project.pbxproj | 614 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ example/ios/Runner/Base.lproj/Main.storyboard | 26 + example/ios/Runner/Info.plist | 49 ++ example/ios/Runner/Runner-Bridging-Header.h | 1 + example/ios/RunnerTests/RunnerTests.swift | 26 + example/lib/main.dart | 63 ++ example/pubspec.lock | 267 ++++++++ example/pubspec.yaml | 85 +++ example/test/widget_test.dart | 27 + ios/.gitignore | 38 ++ ios/Assets/.gitkeep | 0 ios/Classes/ClickstreamFlutterPlugin.swift | 19 + ios/clickstream_flutter.podspec | 23 + lib/clickstream_flutter.dart | 8 + lib/clickstream_flutter_method_channel.dart | 17 + ...lickstream_flutter_platform_interface.dart | 29 + pubspec.yaml | 72 ++ ...ickstream_flutter_method_channel_test.dart | 27 + test/clickstream_flutter_test.dart | 29 + 88 files changed, 2460 insertions(+), 13 deletions(-) create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 CHANGELOG.md create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/build.gradle create mode 100644 android/settings.gradle create mode 100644 android/src/main/AndroidManifest.xml create mode 100644 android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt create mode 100644 android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt create mode 100644 example/.gitignore create mode 100644 example/README.md create mode 100644 example/analysis_options.yaml create mode 100644 example/android/.gitignore create mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/src/debug/AndroidManifest.xml create mode 100644 example/android/app/src/main/AndroidManifest.xml create mode 100644 example/android/app/src/main/kotlin/software/aws/solution/clickstream_flutter_example/MainActivity.kt create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 example/android/app/src/main/res/values-night/styles.xml create mode 100644 example/android/app/src/main/res/values/styles.xml create mode 100644 example/android/app/src/profile/AndroidManifest.xml create mode 100644 example/android/build.gradle create mode 100644 example/android/gradle.properties create mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 example/android/settings.gradle create mode 100644 example/integration_test/plugin_integration_test.dart create mode 100644 example/ios/.gitignore create mode 100644 example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 example/ios/Flutter/Debug.xcconfig create mode 100644 example/ios/Flutter/Release.xcconfig create mode 100644 example/ios/Podfile create mode 100644 example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner/AppDelegate.swift create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 example/ios/Runner/Info.plist create mode 100644 example/ios/Runner/Runner-Bridging-Header.h create mode 100644 example/ios/RunnerTests/RunnerTests.swift create mode 100644 example/lib/main.dart create mode 100644 example/pubspec.lock create mode 100644 example/pubspec.yaml create mode 100644 example/test/widget_test.dart create mode 100644 ios/.gitignore create mode 100644 ios/Assets/.gitkeep create mode 100644 ios/Classes/ClickstreamFlutterPlugin.swift create mode 100644 ios/clickstream_flutter.podspec create mode 100644 lib/clickstream_flutter.dart create mode 100644 lib/clickstream_flutter_method_channel.dart create mode 100644 lib/clickstream_flutter_platform_interface.dart create mode 100644 pubspec.yaml create mode 100644 test/clickstream_flutter_method_channel_test.dart create mode 100644 test/clickstream_flutter_test.dart diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96486fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..7e5bb4c --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ead455963c12b453cdb2358cad34969c76daf180" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + - platform: android + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + - platform: ios + create_revision: ead455963c12b453cdb2358cad34969c76daf180 + base_revision: ead455963c12b453cdb2358cad34969c76daf180 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/README.md b/README.md index 847260c..2b94624 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ -## My Project +# clickstream_flutter -TODO: Fill this README out! +clickstream flutter SDK -Be sure to: +## Getting Started -* Change the title in this README -* Edit your repository description on GitHub - -## Security - -See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. - -## License - -This project is licensed under the Apache-2.0 License. +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..161bdcd --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..1ed3342 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,68 @@ +group 'software.aws.solution.clickstream_flutter' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'software.aws.solution.clickstream_flutter' + } + + compileSdkVersion 33 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + test.java.srcDirs += 'src/test/kotlin' + } + + defaultConfig { + minSdkVersion 19 + } + + dependencies { + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'org.mockito:mockito-core:5.0.0' + } + + testOptions { + unitTests.all { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..4613fad --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'clickstream_flutter' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f5376d0 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt new file mode 100644 index 0000000..15dd9c7 --- /dev/null +++ b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt @@ -0,0 +1,35 @@ +package software.aws.solution.clickstream_flutter + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** ClickstreamFlutterPlugin */ +class ClickstreamFlutterPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "clickstream_flutter") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt b/android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt new file mode 100644 index 0000000..9f69015 --- /dev/null +++ b/android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt @@ -0,0 +1,27 @@ +package software.aws.solution.clickstream_flutter + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import kotlin.test.Test +import org.mockito.Mockito + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +internal class ClickstreamFlutterPluginTest { + @Test + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { + val plugin = ClickstreamFlutterPlugin() + + val call = MethodCall("getPlatformVersion", null) + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) + plugin.onMethodCall(call, mockResult) + + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) + } +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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 +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# 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/README.md b/example/README.md new file mode 100644 index 0000000..fc3f593 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# clickstream_flutter_example + +Demonstrates how to use the clickstream_flutter plugin. + +## 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://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..52a1ddc --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,67 @@ +plugins { + id "com.android.application" + id "kotlin-android" + 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 "software.aws.solution.clickstream_flutter_example" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + 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 "software.aws.solution.clickstream_flutter_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + 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 {} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e0265ed --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/kotlin/software/aws/solution/clickstream_flutter_example/MainActivity.kt b/example/android/app/src/main/kotlin/software/aws/solution/clickstream_flutter_example/MainActivity.kt new file mode 100644 index 0000000..03d1209 --- /dev/null +++ b/example/android/app/src/main/kotlin/software/aws/solution/clickstream_flutter_example/MainActivity.kt @@ -0,0 +1,6 @@ +package software.aws.solution.clickstream_flutter_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} 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/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 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/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..f7eb7f6 --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.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/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..55c4ca8 --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,20 @@ +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 + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} + +include ":app" + +apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/integration_test/plugin_integration_test.dart b/example/integration_test/plugin_integration_test.dart new file mode 100644 index 0000000..9a3690f --- /dev/null +++ b/example/integration_test/plugin_integration_test.dart @@ -0,0 +1,25 @@ +// This is a basic Flutter integration test. +// +// Since integration tests run in a full Flutter application, they can interact +// with the host side of a plugin implementation, unlike Dart unit tests. +// +// For more information about Flutter integration tests, please see +// https://docs.flutter.dev/cookbook/testing/integration/introduction + + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'package:clickstream_flutter/clickstream_flutter.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('getPlatformVersion test', (WidgetTester tester) async { + final ClickstreamFlutter plugin = ClickstreamFlutter(); + final String? version = await plugin.getPlatformVersion(); + // The version string depends on the host platform running the test, so + // just assert that some non-empty string is returned. + expect(version?.isNotEmpty, true); + }); +} diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +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/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9625e10 --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#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 new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..fdcc671 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +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) + end +end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d300eae --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,614 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* 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 */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 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 */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; +/* 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; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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 = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 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 = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807E294A63A400263BE5 /* Frameworks */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + 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; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 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"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* 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; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = software.aws.solution.clickstreamFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* 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 = software.aws.solution.clickstreamFlutterExample.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 = 89B67EB44CE7B6631473024E /* 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 = software.aws.solution.clickstreamFlutterExample.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 = 640959BDD8F10B91D80A66BE /* 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 = software.aws.solution.clickstreamFlutterExample.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; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = software.aws.solution.clickstreamFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = software.aws.solution.clickstreamFlutterExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* 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 = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..87131a0 --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_xN#0001NP)t-s|Ns9~ z#rXRE|M&d=0au&!`~QyF`q}dRnBDt}*!qXo`c{v z{Djr|@Adh0(D_%#_&mM$D6{kE_x{oE{l@J5@%H*?%=t~i_`ufYOPkAEn!pfkr2$fs z652Tz0001XNklqeeKN4RM4i{jKqmiC$?+xN>3Apn^ z0QfuZLym_5b<*QdmkHjHlj811{If)dl(Z2K0A+ekGtrFJb?g|wt#k#pV-#A~bK=OT ts8>{%cPtyC${m|1#B1A6#u!Q;umknL1chzTM$P~L002ovPDHLkV1lTfnu!1a literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 zcmV;526g#~P){YQnis^a@{&-nmRmq)<&%Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT> z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g( z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=# zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~ z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{` zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550 z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8 z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~ z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2 z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H= zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f% z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`? zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91 z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a} z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3< zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7 zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9 zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5&#r7J#c`3Z7x!LpTc01dx zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..b111518 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Clickstream Flutter + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + clickstream_flutter_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..eadca00 --- /dev/null +++ b/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,26 @@ +import Flutter +import UIKit +import XCTest + +@testable import clickstream_flutter + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = ClickstreamFlutterPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..8de47ab --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:clickstream_flutter/clickstream_flutter.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + final _clickstreamFlutterPlugin = ClickstreamFlutter(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await _clickstreamFlutterPlugin.getPlatformVersion() ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..ff94088 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,267 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clickstream_flutter: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" + source: hosted + version: "1.17.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + url: "https://pub.dev" + source: hosted + version: "2.1.6" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + url: "https://pub.dev" + source: hosted + version: "11.7.1" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + url: "https://pub.dev" + source: hosted + version: "3.0.2" +sdks: + dart: ">=3.1.3 <4.0.0" + flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..2cbc787 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: clickstream_flutter_example +description: Demonstrates how to use the clickstream_flutter plugin. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.1.3 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + clickstream_flutter: + # When depending on this package from a real application you should use: + # clickstream_flutter: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + integration_test: + sdk: flutter + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.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 packages. +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 diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..a56ab1a --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. 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:clickstream_flutter_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..0c88507 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,38 @@ +.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 +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ios/Classes/ClickstreamFlutterPlugin.swift b/ios/Classes/ClickstreamFlutterPlugin.swift new file mode 100644 index 0000000..2b1b25e --- /dev/null +++ b/ios/Classes/ClickstreamFlutterPlugin.swift @@ -0,0 +1,19 @@ +import Flutter +import UIKit + +public class ClickstreamFlutterPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "clickstream_flutter", binaryMessenger: registrar.messenger()) + let instance = ClickstreamFlutterPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/ios/clickstream_flutter.podspec b/ios/clickstream_flutter.podspec new file mode 100644 index 0000000..bfa8906 --- /dev/null +++ b/ios/clickstream_flutter.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint clickstream_flutter.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'clickstream_flutter' + s.version = '0.0.1' + s.summary = 'clickstream flutter SDK' + s.description = <<-DESC +clickstream flutter SDK + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/lib/clickstream_flutter.dart b/lib/clickstream_flutter.dart new file mode 100644 index 0000000..cb4cbbd --- /dev/null +++ b/lib/clickstream_flutter.dart @@ -0,0 +1,8 @@ + +import 'clickstream_flutter_platform_interface.dart'; + +class ClickstreamFlutter { + Future getPlatformVersion() { + return ClickstreamFlutterPlatform.instance.getPlatformVersion(); + } +} diff --git a/lib/clickstream_flutter_method_channel.dart b/lib/clickstream_flutter_method_channel.dart new file mode 100644 index 0000000..2737894 --- /dev/null +++ b/lib/clickstream_flutter_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'clickstream_flutter_platform_interface.dart'; + +/// An implementation of [ClickstreamFlutterPlatform] that uses method channels. +class MethodChannelClickstreamFlutter extends ClickstreamFlutterPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('clickstream_flutter'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/lib/clickstream_flutter_platform_interface.dart b/lib/clickstream_flutter_platform_interface.dart new file mode 100644 index 0000000..92a7454 --- /dev/null +++ b/lib/clickstream_flutter_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'clickstream_flutter_method_channel.dart'; + +abstract class ClickstreamFlutterPlatform extends PlatformInterface { + /// Constructs a ClickstreamFlutterPlatform. + ClickstreamFlutterPlatform() : super(token: _token); + + static final Object _token = Object(); + + static ClickstreamFlutterPlatform _instance = MethodChannelClickstreamFlutter(); + + /// The default instance of [ClickstreamFlutterPlatform] to use. + /// + /// Defaults to [MethodChannelClickstreamFlutter]. + static ClickstreamFlutterPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [ClickstreamFlutterPlatform] when + /// they register themselves. + static set instance(ClickstreamFlutterPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..f64e36e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,72 @@ +name: clickstream_flutter +description: clickstream flutter SDK +version: 0.0.1 +homepage: + +environment: + sdk: '>=3.1.3 <4.0.0' + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.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 packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: software.aws.solution.clickstream_flutter + pluginClass: ClickstreamFlutterPlugin + ios: + pluginClass: ClickstreamFlutterPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, 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 in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/clickstream_flutter_method_channel_test.dart b/test/clickstream_flutter_method_channel_test.dart new file mode 100644 index 0000000..fb98b46 --- /dev/null +++ b/test/clickstream_flutter_method_channel_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelClickstreamFlutter platform = MethodChannelClickstreamFlutter(); + const MethodChannel channel = MethodChannel('clickstream_flutter'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/test/clickstream_flutter_test.dart b/test/clickstream_flutter_test.dart new file mode 100644 index 0000000..2805eaf --- /dev/null +++ b/test/clickstream_flutter_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:clickstream_flutter/clickstream_flutter.dart'; +import 'package:clickstream_flutter/clickstream_flutter_platform_interface.dart'; +import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockClickstreamFlutterPlatform + with MockPlatformInterfaceMixin + implements ClickstreamFlutterPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final ClickstreamFlutterPlatform initialPlatform = ClickstreamFlutterPlatform.instance; + + test('$MethodChannelClickstreamFlutter is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + ClickstreamFlutter clickstreamFlutterPlugin = ClickstreamFlutter(); + MockClickstreamFlutterPlatform fakePlatform = MockClickstreamFlutterPlatform(); + ClickstreamFlutterPlatform.instance = fakePlatform; + + expect(await clickstreamFlutterPlugin.getPlatformVersion(), '42'); + }); +} From 53a21871189a01652879544f139d9fdc227a7fbe Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Thu, 19 Oct 2023 09:05:25 +0800 Subject: [PATCH 2/5] feat: finish android SDK integrate. --- android/build.gradle | 12 +- .../ClickstreamFlutterPlugin.kt | 79 +++++++--- .../main/res/raw/amplifyconfiguration.json | 15 ++ .../android/app/src/main/AndroidManifest.xml | 1 + .../app/FlutterMultiDexApplication.java | 25 ++++ example/ios/Podfile.lock | 28 ++++ example/ios/Runner.xcodeproj/project.pbxproj | 138 ++++++++++++++++-- .../contents.xcworkspacedata | 3 + example/lib/main.dart | 14 +- lib/clickstream_flutter.dart | 11 +- lib/clickstream_flutter_method_channel.dart | 17 ++- ...lickstream_flutter_platform_interface.dart | 26 ++-- ...ickstream_flutter_method_channel_test.dart | 2 +- test/clickstream_flutter_test.dart | 20 ++- 14 files changed, 324 insertions(+), 67 deletions(-) create mode 100644 android/src/main/res/raw/amplifyconfiguration.json create mode 100644 example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java create mode 100644 example/ios/Podfile.lock diff --git a/android/build.gradle b/android/build.gradle index 1ed3342..40e7772 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'software.aws.solution.clickstream_flutter' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -52,6 +52,8 @@ android { dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation 'org.mockito:mockito-core:5.0.0' + implementation 'software.aws.solution:clickstream:0.9.0' + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) } testOptions { @@ -59,9 +61,9 @@ android { useJUnitPlatform() testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen { false } + showStandardStreams = true } } } diff --git a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt index 15dd9c7..ebb1b2f 100644 --- a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt +++ b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt @@ -1,35 +1,68 @@ package software.aws.solution.clickstream_flutter -import androidx.annotation.NonNull - +import android.app.Activity import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import software.aws.solution.clickstream.ClickstreamAnalytics + /** ClickstreamFlutterPlugin */ -class ClickstreamFlutterPlugin: FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel : MethodChannel - - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "clickstream_flutter") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(call: MethodCall, result: Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${android.os.Build.VERSION.RELEASE}") - } else { - result.notImplemented() +class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + + private var mActivity: Activity? = null + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "clickstream_flutter") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else if (call.method == "init") { + if (mActivity != null) { + ClickstreamAnalytics.init(mActivity!!.applicationContext) + ClickstreamAnalytics.getClickStreamConfiguration() + .withAppId("shopping") + .withEndpoint("http://Clicks-Inges-m6f4WJ0DDSWv-478806672.us-east-1.elb.amazonaws.com/collect") + .withLogEvents(true) + result.success(true) + } else { + result.success(false) + } + } else if (call.method == "record") { + ClickstreamAnalytics.recordEvent(call.arguments.toString()) + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) } - } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + mActivity = binding.activity + } + + override fun onDetachedFromActivityForConfigChanges() { + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + mActivity = binding.activity + } + + override fun onDetachedFromActivity() { + mActivity = null + } } diff --git a/android/src/main/res/raw/amplifyconfiguration.json b/android/src/main/res/raw/amplifyconfiguration.json new file mode 100644 index 0000000..c1f3658 --- /dev/null +++ b/android/src/main/res/raw/amplifyconfiguration.json @@ -0,0 +1,15 @@ +{ + "UserAgent": "aws-solution/clickstream", + "Version": "1.0", + "analytics": { + "plugins": { + "awsClickstreamPlugin": { + "appId": "", + "endpoint": "", + "isCompressEvents": true, + "autoFlushEventsInterval": 10000, + "isTrackAppExceptionEvents": false + } + } + } +} \ No newline at end of file diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index e0265ed..6297515 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ /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; + }; + F44AF16B91A56D9AE7C30FF8 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + 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"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -377,7 +485,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 54FD31DBEE6DB689C452D8F0 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -395,7 +503,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = BE1462DFA5ED46118019E3B4 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,7 +519,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 6BFEB1AFE6CE2C78605A0A8C /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/lib/main.dart b/example/lib/main.dart index 8de47ab..07721f2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -17,12 +17,20 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { String _platformVersion = 'Unknown'; - final _clickstreamFlutterPlugin = ClickstreamFlutter(); + final analytics = ClickstreamFlutter(); @override void initState() { super.initState(); initPlatformState(); + initSDK(); + } + + Future initSDK() async { + bool initResult = await analytics.init(); + if (initResult) { + analytics.record("testEvent"); + } } // Platform messages are asynchronous, so we initialize in an async method. @@ -31,8 +39,8 @@ class _MyAppState extends State { // Platform messages may fail, so we use a try/catch PlatformException. // We also handle the message potentially returning null. try { - platformVersion = - await _clickstreamFlutterPlugin.getPlatformVersion() ?? 'Unknown platform version'; + platformVersion = await analytics.getPlatformVersion() ?? + 'Unknown platform version'; } on PlatformException { platformVersion = 'Failed to get platform version.'; } diff --git a/lib/clickstream_flutter.dart b/lib/clickstream_flutter.dart index cb4cbbd..3fcb72e 100644 --- a/lib/clickstream_flutter.dart +++ b/lib/clickstream_flutter.dart @@ -1,8 +1,15 @@ - import 'clickstream_flutter_platform_interface.dart'; class ClickstreamFlutter { Future getPlatformVersion() { - return ClickstreamFlutterPlatform.instance.getPlatformVersion(); + return ClickstreamAnalytics.instance.getPlatformVersion(); + } + + Future init() { + return ClickstreamAnalytics.instance.init(); + } + + Future record(String eventName) { + return ClickstreamAnalytics.instance.record(eventName); } } diff --git a/lib/clickstream_flutter_method_channel.dart b/lib/clickstream_flutter_method_channel.dart index 2737894..fa650b7 100644 --- a/lib/clickstream_flutter_method_channel.dart +++ b/lib/clickstream_flutter_method_channel.dart @@ -4,14 +4,27 @@ import 'package:flutter/services.dart'; import 'clickstream_flutter_platform_interface.dart'; /// An implementation of [ClickstreamFlutterPlatform] that uses method channels. -class MethodChannelClickstreamFlutter extends ClickstreamFlutterPlatform { +class MethodChannelClickstreamAnalytics extends ClickstreamAnalytics { /// The method channel used to interact with the native platform. @visibleForTesting final methodChannel = const MethodChannel('clickstream_flutter'); @override Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); + final version = + await methodChannel.invokeMethod('getPlatformVersion'); return version; } + + @override + Future init() async { + final result = await methodChannel.invokeMethod('init'); + return result ?? false; + } + + @override + Future record(String eventName) async { + final result = await methodChannel.invokeMethod('record', eventName); + return result ?? false; + } } diff --git a/lib/clickstream_flutter_platform_interface.dart b/lib/clickstream_flutter_platform_interface.dart index 92a7454..3efaae2 100644 --- a/lib/clickstream_flutter_platform_interface.dart +++ b/lib/clickstream_flutter_platform_interface.dart @@ -2,23 +2,23 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'clickstream_flutter_method_channel.dart'; -abstract class ClickstreamFlutterPlatform extends PlatformInterface { - /// Constructs a ClickstreamFlutterPlatform. - ClickstreamFlutterPlatform() : super(token: _token); +abstract class ClickstreamAnalytics extends PlatformInterface { + /// Constructs a ClickstreamAnalytics. + ClickstreamAnalytics() : super(token: _token); static final Object _token = Object(); - static ClickstreamFlutterPlatform _instance = MethodChannelClickstreamFlutter(); + static ClickstreamAnalytics _instance = MethodChannelClickstreamAnalytics(); - /// The default instance of [ClickstreamFlutterPlatform] to use. + /// The default instance of [ClickstreamAnalytics] to use. /// - /// Defaults to [MethodChannelClickstreamFlutter]. - static ClickstreamFlutterPlatform get instance => _instance; + /// Defaults to [MethodChannelClickstreamAnalytics]. + static ClickstreamAnalytics get instance => _instance; /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [ClickstreamFlutterPlatform] when + /// platform-specific class that extends [ClickstreamAnalytics] when /// they register themselves. - static set instance(ClickstreamFlutterPlatform instance) { + static set instance(ClickstreamAnalytics instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } @@ -26,4 +26,12 @@ abstract class ClickstreamFlutterPlatform extends PlatformInterface { Future getPlatformVersion() { throw UnimplementedError('platformVersion() has not been implemented.'); } + + Future init() { + throw UnimplementedError('init() has not been implemented.'); + } + + Future record(String eventName) { + throw UnimplementedError('platformVersion() has not been implemented.'); + } } diff --git a/test/clickstream_flutter_method_channel_test.dart b/test/clickstream_flutter_method_channel_test.dart index fb98b46..a247419 100644 --- a/test/clickstream_flutter_method_channel_test.dart +++ b/test/clickstream_flutter_method_channel_test.dart @@ -5,7 +5,7 @@ import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelClickstreamFlutter platform = MethodChannelClickstreamFlutter(); + MethodChannelClickstreamAnalytics platform = MethodChannelClickstreamAnalytics(); const MethodChannel channel = MethodChannel('clickstream_flutter'); setUp(() { diff --git a/test/clickstream_flutter_test.dart b/test/clickstream_flutter_test.dart index 2805eaf..d958863 100644 --- a/test/clickstream_flutter_test.dart +++ b/test/clickstream_flutter_test.dart @@ -6,23 +6,29 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockClickstreamFlutterPlatform with MockPlatformInterfaceMixin - implements ClickstreamFlutterPlatform { - + implements ClickstreamAnalytics { @override Future getPlatformVersion() => Future.value('42'); + + @override + Future init() => Future.value(true); + + @override + Future record(String name) => Future.value(); } void main() { - final ClickstreamFlutterPlatform initialPlatform = ClickstreamFlutterPlatform.instance; + final ClickstreamAnalytics initialPlatform = ClickstreamAnalytics.instance; - test('$MethodChannelClickstreamFlutter is the default instance', () { - expect(initialPlatform, isInstanceOf()); + test('$MethodChannelClickstreamAnalytics is the default instance', () { + expect(initialPlatform, isInstanceOf()); }); test('getPlatformVersion', () async { ClickstreamFlutter clickstreamFlutterPlugin = ClickstreamFlutter(); - MockClickstreamFlutterPlatform fakePlatform = MockClickstreamFlutterPlatform(); - ClickstreamFlutterPlatform.instance = fakePlatform; + MockClickstreamFlutterPlatform fakePlatform = + MockClickstreamFlutterPlatform(); + ClickstreamAnalytics.instance = fakePlatform; expect(await clickstreamFlutterPlugin.getPlatformVersion(), '42'); }); From c79d15d4ecfd6f3545796500cba8611d87582a93 Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Fri, 20 Oct 2023 09:09:52 +0800 Subject: [PATCH 3/5] feat: finish swift SDK integrate. --- .gitmodules | 3 + .../ClickstreamFlutterPlugin.kt | 246 ++++++++++++++++-- example/android/app/build.gradle | 1 + .../app/FlutterMultiDexApplication.java | 12 +- .../plugin_integration_test.dart | 11 +- example/ios/Podfile.lock | 24 +- example/ios/Runner.xcodeproj/project.pbxproj | 4 +- example/ios/Runner/Info.plist | 5 + example/lib/main.dart | 128 +++++++-- .../Assets}/amplifyconfiguration.json | 0 ios/Classes/ClickstreamFlutterPlugin.swift | 165 +++++++++++- ios/Clickstream | 1 + ios/amplifyconfiguration.json | 15 ++ ios/clickstream_flutter.podspec | 11 +- lib/clickstream_flutter.dart | 84 +++++- lib/clickstream_flutter_method_channel.dart | 44 +++- ...lickstream_flutter_platform_interface.dart | 48 +++- ...ickstream_flutter_method_channel_test.dart | 104 +++++++- test/clickstream_flutter_test.dart | 112 ++++++-- 19 files changed, 900 insertions(+), 118 deletions(-) create mode 100644 .gitmodules rename {android/src/main/res/raw => ios/Assets}/amplifyconfiguration.json (100%) create mode 160000 ios/Clickstream create mode 100644 ios/amplifyconfiguration.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6bae918 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ios/Clickstream"] + path = ios/Clickstream + url = https://github.com/awslabs/clickstream-swift diff --git a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt index ebb1b2f..1adc22b 100644 --- a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt +++ b/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt @@ -1,6 +1,11 @@ package software.aws.solution.clickstream_flutter import android.app.Activity +import com.amazonaws.logging.Log +import com.amazonaws.logging.LogFactory +import com.amplifyframework.AmplifyException +import com.amplifyframework.core.Amplify +import com.amplifyframework.core.AmplifyConfiguration import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -8,7 +13,16 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import org.json.JSONObject +import software.aws.solution.clickstream.AWSClickstreamPlugin import software.aws.solution.clickstream.ClickstreamAnalytics +import software.aws.solution.clickstream.ClickstreamAttribute +import software.aws.solution.clickstream.ClickstreamEvent +import software.aws.solution.clickstream.ClickstreamUserAttribute +import software.aws.solution.clickstream.client.util.ThreadUtil +import java.util.Objects +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors /** ClickstreamFlutterPlugin */ @@ -17,6 +31,12 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware /// /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity + + private val cachedThreadPool: ExecutorService by lazy { Executors.newCachedThreadPool() } + + private val log: Log = LogFactory.getLog( + ClickstreamFlutterPlugin::class.java + ) private lateinit var channel: MethodChannel private var mActivity: Activity? = null @@ -27,26 +47,222 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware } override fun onMethodCall(call: MethodCall, result: Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${android.os.Build.VERSION.RELEASE}") - } else if (call.method == "init") { - if (mActivity != null) { - ClickstreamAnalytics.init(mActivity!!.applicationContext) - ClickstreamAnalytics.getClickStreamConfiguration() - .withAppId("shopping") - .withEndpoint("http://Clicks-Inges-m6f4WJ0DDSWv-478806672.us-east-1.elb.amazonaws.com/collect") - .withLogEvents(true) - result.success(true) - } else { - result.success(false) + val arguments = call.arguments() as HashMap? + when (call.method) { + "init" -> { + result.success(initSDK(arguments!!)) + } + + "record" -> { + recordEvent(arguments) + } + + "setUserId" -> { + setUserId(arguments) + } + + "setUserAttributes" -> { + setUserAttributes(arguments) + } + + "setGlobalAttributes" -> { + setGlobalAttributes(arguments) + } + + "deleteGlobalAttributes" -> { + deleteGlobalAttributes(arguments) + } + + "updateConfigure" -> { + updateConfigure(arguments) } - } else if (call.method == "record") { - ClickstreamAnalytics.recordEvent(call.arguments.toString()) + + "flushEvents" -> { + ClickstreamAnalytics.flushEvents() + } + + else -> { + result.notImplemented() + } + } + } + + + private fun initSDK(arguments: HashMap): Boolean { + if (getIsInitialized()) return false + if (mActivity != null) { + val context = mActivity!!.applicationContext + if (ThreadUtil.notInMainThread()) { + log.error("Clickstream SDK initialization failed, please initialize in the main thread") + return false + } + val amplifyObject = JSONObject() + val analyticsObject = JSONObject() + val pluginsObject = JSONObject() + val awsClickstreamPluginObject = JSONObject() + awsClickstreamPluginObject.put("appId", arguments["appId"]) + awsClickstreamPluginObject.put("endpoint", arguments["endpoint"]) + pluginsObject.put("awsClickstreamPlugin", awsClickstreamPluginObject) + analyticsObject.put("plugins", pluginsObject) + amplifyObject.put("analytics", analyticsObject) + val configure = AmplifyConfiguration.fromJson(amplifyObject) + try { + Amplify.addPlugin(AWSClickstreamPlugin(context)) + Amplify.configure(configure, context) + } catch (exception: AmplifyException) { + log.error("Clickstream SDK initialization failed with error: " + exception.message) + return false + } + val sessionTimeoutDuration = arguments["sessionTimeoutDuration"] + .let { (it as? Int)?.toLong() ?: (it as Long) } + val sendEventsInterval = arguments["sendEventsInterval"] + .let { (it as? Int)?.toLong() ?: (it as Long) } + ClickstreamAnalytics.getClickStreamConfiguration() + .withLogEvents(arguments["isLogEvents"] as Boolean) + .withTrackScreenViewEvents(arguments["isTrackScreenViewEvents"] as Boolean) + .withTrackUserEngagementEvents(arguments["isTrackUserEngagementEvents"] as Boolean) + .withTrackAppExceptionEvents(arguments["isTrackAppExceptionEvents"] as Boolean) + .withSendEventsInterval(sendEventsInterval) + .withSessionTimeoutDuration(sessionTimeoutDuration) + .withCompressEvents(arguments["isCompressEvents"] as Boolean) + .withAuthCookie(arguments["authCookie"] as String) + return true } else { - result.notImplemented() + return false + } + } + + private fun recordEvent(arguments: HashMap?) { + cachedThreadPool.execute { + arguments?.let { + val eventName = it["eventName"] as String + val attributes = it["attributes"] as HashMap<*, *> + val eventBuilder = ClickstreamEvent.builder().name(eventName) + for ((key, value) in attributes) { + if (value is String) { + eventBuilder.add(key.toString(), value) + } else if (value is Double) { + eventBuilder.add(key.toString(), value) + } else if (value is Boolean) { + eventBuilder.add(key.toString(), value) + } else if (value is Int) { + eventBuilder.add(key.toString(), value) + } else if (value is Long) { + eventBuilder.add(key.toString(), value) + } + } + ClickstreamAnalytics.recordEvent(eventBuilder.build()) + } + } + } + + + private fun setUserId(arguments: java.util.HashMap?) { + arguments?.let { + val userId = arguments["userId"] + if (userId == null) { + ClickstreamAnalytics.setUserId(null) + } else { + ClickstreamAnalytics.setUserId(userId.toString()) + } + } + } + + private fun setUserAttributes(arguments: java.util.HashMap?) { + arguments?.let { + val builder = ClickstreamUserAttribute.Builder() + for ((key, value) in arguments) { + if (value is String) { + builder.add(key, value) + } else if (value is Double) { + builder.add(key, value) + } else if (value is Boolean) { + builder.add(key, value) + } else if (value is Int) { + builder.add(key, value) + } else if (value is Long) { + builder.add(key, value) + } + } + ClickstreamAnalytics.addUserAttributes(builder.build()) } } + private fun setGlobalAttributes(arguments: java.util.HashMap?) { + arguments?.let { + val builder = ClickstreamAttribute.Builder() + for ((key, value) in arguments) { + if (value is String) { + builder.add(key, value) + } else if (value is Double) { + builder.add(key, value) + } else if (value is Boolean) { + builder.add(key, value) + } else if (value is Int) { + builder.add(key, value) + } else if (value is Long) { + builder.add(key, value) + } + } + ClickstreamAnalytics.addGlobalAttributes(builder.build()) + } + } + + private fun deleteGlobalAttributes(arguments: java.util.HashMap?) { + arguments?.let { + @Suppress("UNCHECKED_CAST") + val attributes = arguments["attributes"] as ArrayList + ClickstreamAnalytics.deleteGlobalAttributes(*attributes.toTypedArray()) + } + } + + private fun updateConfigure(arguments: java.util.HashMap?) { + arguments?.let { + val configure = ClickstreamAnalytics.getClickStreamConfiguration() + arguments["appId"]?.let { + configure.withAppId(it as String) + } + arguments["endpoint"]?.let { + configure.withEndpoint(it as String) + } + arguments["isLogEvents"]?.let { + configure.withLogEvents(it as Boolean) + } + arguments["isTrackScreenViewEvents"]?.let { + configure.withTrackScreenViewEvents(it as Boolean) + } + arguments["isTrackUserEngagementEvents"]?.let { + configure.withTrackUserEngagementEvents(it as Boolean) + } + arguments["isTrackAppExceptionEvents"]?.let { + configure.withTrackAppExceptionEvents(it as Boolean) + } + arguments["sessionTimeoutDuration"]?.let { + val sessionTimeoutDuration = arguments["sessionTimeoutDuration"] + .let { (it as? Int)?.toLong() ?: (it as Long) } + configure.withSessionTimeoutDuration(sessionTimeoutDuration) + } + arguments["isCompressEvents"]?.let { + configure.withCompressEvents(it as Boolean) + } + arguments["authCookie"]?.let { + configure.withAuthCookie(it as String) + } + } + } + + private fun getIsInitialized(): Boolean { + return invokeSuperMethod(Amplify.Analytics, "isConfigured") as Boolean + } + + @Throws(Exception::class) + fun invokeSuperMethod(`object`: Any, methodName: String): Any? { + val method = + Objects.requireNonNull(`object`.javaClass.superclass).getDeclaredMethod(methodName) + method.isAccessible = true + return method.invoke(`object`) + } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 52a1ddc..f44b255 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -49,6 +49,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + multiDexEnabled true } buildTypes { diff --git a/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java index 752fc18..6c5d303 100644 --- a/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java +++ b/example/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java @@ -10,16 +10,14 @@ import android.app.Application; import android.content.Context; import androidx.annotation.CallSuper; -import androidx.multidex.MultiDex; /** * Extension of {@link android.app.Application}, adding multidex support. */ public class FlutterMultiDexApplication extends Application { - @Override - @CallSuper - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - MultiDex.install(this); - } + @Override + @CallSuper + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + } } diff --git a/example/integration_test/plugin_integration_test.dart b/example/integration_test/plugin_integration_test.dart index 9a3690f..953f468 100644 --- a/example/integration_test/plugin_integration_test.dart +++ b/example/integration_test/plugin_integration_test.dart @@ -15,11 +15,10 @@ import 'package:clickstream_flutter/clickstream_flutter.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('getPlatformVersion test', (WidgetTester tester) async { - final ClickstreamFlutter plugin = ClickstreamFlutter(); - final String? version = await plugin.getPlatformVersion(); - // The version string depends on the host platform running the test, so - // just assert that some non-empty string is returned. - expect(version?.isNotEmpty, true); + testWidgets('test init SDK success', (WidgetTester tester) async { + final ClickstreamAnalytics analytics = ClickstreamAnalytics(); + final bool result = await analytics.init( + appId: "testAppId", endpoint: "https://example.com/collect"); + expect(result, true); }); } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index fb5dd2e..e74f0d5 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,15 +1,34 @@ PODS: + - Amplify (1.30.3): + - Amplify/Default (= 1.30.3) + - Amplify/Default (1.30.3) - clickstream_flutter (0.0.1): + - clickstream_flutter/Clickstream (= 0.0.1) - Flutter + - clickstream_flutter/Clickstream (0.0.1): + - Amplify (= 1.30.3) + - Flutter + - GzipSwift (= 5.1.1) + - SQLite.swift (= 0.13.2) - Flutter (1.0.0) + - GzipSwift (5.1.1) - integration_test (0.0.1): - Flutter + - SQLite.swift (0.13.2): + - SQLite.swift/standard (= 0.13.2) + - SQLite.swift/standard (0.13.2) DEPENDENCIES: - clickstream_flutter (from `.symlinks/plugins/clickstream_flutter/ios`) - Flutter (from `Flutter`) - integration_test (from `.symlinks/plugins/integration_test/ios`) +SPEC REPOS: + trunk: + - Amplify + - GzipSwift + - SQLite.swift + EXTERNAL SOURCES: clickstream_flutter: :path: ".symlinks/plugins/clickstream_flutter/ios" @@ -19,9 +38,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/integration_test/ios" SPEC CHECKSUMS: - clickstream_flutter: 2df0c1ea137129668289dc8ab4d4a1394ee246b9 + Amplify: 516e5da5f256f62841b6bc659e1644bc999d7b6e + clickstream_flutter: d5dd3ad04cb641b27c1014c23b4cdbc98f6956dc Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa integration_test: 13825b8a9334a850581300559b8839134b124670 + SQLite.swift: 4fc2be46c36392e3b87afe6fe7f1801c1daa07ef PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d518381..f6eb55d 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -105,7 +105,6 @@ BE1462DFA5ED46118019E3B4 /* Pods-RunnerTests.release.xcconfig */, 6BFEB1AFE6CE2C78605A0A8C /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -471,6 +470,7 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -649,6 +649,7 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -671,6 +672,7 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index b111518..4a5ecce 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/example/lib/main.dart b/example/lib/main.dart index 07721f2..22777b5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,21 +16,13 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - String _platformVersion = 'Unknown'; - final analytics = ClickstreamFlutter(); + String testEndpoint = "https://example.com/collect"; + final analytics = ClickstreamAnalytics(); @override void initState() { super.initState(); initPlatformState(); - initSDK(); - } - - Future initSDK() async { - bool initResult = await analytics.init(); - if (initResult) { - analytics.record("testEvent"); - } } // Platform messages are asynchronous, so we initialize in an async method. @@ -39,8 +31,7 @@ class _MyAppState extends State { // Platform messages may fail, so we use a try/catch PlatformException. // We also handle the message potentially returning null. try { - platformVersion = await analytics.getPlatformVersion() ?? - 'Unknown platform version'; + platformVersion = 'Unknown platform version'; } on PlatformException { platformVersion = 'Failed to get platform version.'; } @@ -49,10 +40,6 @@ class _MyAppState extends State { // message was in flight, we want to discard the reply rather than calling // setState to update our non-existent appearance. if (!mounted) return; - - setState(() { - _platformVersion = platformVersion; - }); } @override @@ -60,10 +47,113 @@ class _MyAppState extends State { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('Clickstream Flutter SDK API'), ), - body: Center( - child: Text('Running on: $_platformVersion\n'), + body: ListView( + children: [ + ListTile( + leading: const Icon(Icons.not_started_outlined), + title: const Text('initSDK'), + onTap: () async { + var result = await analytics.init( + appId: "shopping", + endpoint: testEndpoint, + isLogEvents: true, + isCompressEvents: false); + print("init SDK result is:$result"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.touch_app), + title: const Text('recordEvent'), + onTap: () async { + analytics.record(name: "testEventWithName"); + analytics.record(name: "testEvent", attributes: { + "category": 'shoes', + "currency": 'CNY', + "intValue": 13, + "longValue": 9999999913991919, + "doubleValue": 11.1234567890121213, + "boolValue": true, + "value": 279.9 + }); + print("recorded testEvent and testEventWithName"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.account_circle), + title: const Text('setUserId'), + onTap: () async { + analytics.setUserId("12345"); + analytics.setUserId(null); + print("setUserId"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.manage_accounts), + title: const Text('setUserAttributes'), + onTap: () async { + analytics.setUserAttributes( + {"category": 'shoes', "currency": 'CNY', "value": 279.9}); + analytics.setUserAttributes({}); + analytics.setUserAttributes({"testNull": null}); + print("setUserAttributes"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.add_circle), + title: const Text('addGlobalAttributes'), + onTap: () async { + analytics.addGlobalAttributes({ + "_channel": "Samsung", + "Class": 5, + "isTrue": true, + "Score": 24.32 + }); + print("addGlobalAttributes"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.delete_rounded), + title: const Text('deleteGlobalAttributes'), + onTap: () async { + analytics.deleteGlobalAttributes(["Score", "_channel"]); + print("deleteGlobalAttributes Score and _channel"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.update), + title: const Text('updateConfigure'), + onTap: () async { + analytics.updateConfigure( + isLogEvents: true, + isCompressEvents: false, + sessionTimeoutDuration: 100000, + isTrackUserEngagementEvents: false, + isTrackAppExceptionEvents: false, + authCookie: "test cookie", + isTrackScreenViewEvents: false); + analytics.updateConfigure(); + print("updateConfigure"); + }, + minLeadingWidth: 0, + ), + ListTile( + leading: const Icon(Icons.send), + title: const Text('flushEvents'), + onTap: () async { + analytics.flushEvents(); + print("flushEvents"); + }, + minLeadingWidth: 0, + ), + ], ), ), ); diff --git a/android/src/main/res/raw/amplifyconfiguration.json b/ios/Assets/amplifyconfiguration.json similarity index 100% rename from android/src/main/res/raw/amplifyconfiguration.json rename to ios/Assets/amplifyconfiguration.json diff --git a/ios/Classes/ClickstreamFlutterPlugin.swift b/ios/Classes/ClickstreamFlutterPlugin.swift index 2b1b25e..f78a6a6 100644 --- a/ios/Classes/ClickstreamFlutterPlugin.swift +++ b/ios/Classes/ClickstreamFlutterPlugin.swift @@ -1,19 +1,156 @@ +import Amplify import Flutter import UIKit public class ClickstreamFlutterPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "clickstream_flutter", binaryMessenger: registrar.messenger()) - let instance = ClickstreamFlutterPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) - default: - result(FlutterMethodNotImplemented) - } - } + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "clickstream_flutter", binaryMessenger: registrar.messenger()) + let instance = ClickstreamFlutterPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + case "init": + result(initSDK(call.arguments as! [String: Any])) + case "record": + recordEvent(call.arguments as! [String: Any]) + case "setUserId": + setUserId(call.arguments as! [String: Any]) + case "setUserAttributes": + setUserAttributes(call.arguments as! [String: Any]) + case "addGlobalAttributes": + addGlobalAttributes(call.arguments as! [String: Any]) + case "deleteGlobalAttributes": + deleteGlobalAttributes(call.arguments as! [String: Any]) + case "updateConfigure": + updateConfigure(call.arguments as! [String: Any]) + case "flushEvents": + ClickstreamAnalytics.flushEvents() + default: + result(FlutterMethodNotImplemented) + } + } + + func initSDK(_ arguments: [String: Any]) -> Bool { + do { + let plugins: [String: JSONValue] = [ + "awsClickstreamPlugin": [ + "appId": JSONValue.string(arguments["appId"] as! String), + "endpoint": JSONValue.string(arguments["endpoint"] as! String), + "isCompressEvents": JSONValue.boolean(arguments["isCompressEvents"] as! Bool), + "autoFlushEventsInterval": JSONValue.number(arguments["sendEventsInterval"] as! Double), + "isTrackAppExceptionEvents": JSONValue.boolean(arguments["isTrackAppExceptionEvents"] as! Bool) + ] + ] + let analyticsConfiguration = AnalyticsCategoryConfiguration(plugins: plugins) + let config = AmplifyConfiguration(analytics: analyticsConfiguration) + try Amplify.add(plugin: AWSClickstreamPlugin()) + try Amplify.configure(config) + let configure = try ClickstreamAnalytics.getClickstreamConfiguration() + configure.isLogEvents = arguments["isLogEvents"] as! Bool + configure.isTrackScreenViewEvents = arguments["isTrackScreenViewEvents"] as! Bool + configure.isTrackUserEngagementEvents = arguments["isTrackUserEngagementEvents"] as! Bool + configure.sessionTimeoutDuration = arguments["sessionTimeoutDuration"] as! Int64 + configure.authCookie = arguments["authCookie"] as? String + return true + } catch { + log.error("Fail to initialize ClickstreamAnalytics: \(error)") + return false + } + } + + func recordEvent(_ arguments: [String: Any]) { + let eventName = arguments["eventName"] as! String + let attributes = arguments["attributes"] as! [String: Any] + if attributes.count > 0 { + ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes)) + } else { + ClickstreamAnalytics.recordEvent(eventName) + } + } + + func setUserId(_ arguments: [String: Any]) { + if arguments["userId"] is NSNull { + ClickstreamAnalytics.setUserId(nil) + } else { + ClickstreamAnalytics.setUserId(arguments["userId"] as? String) + } + } + + func setUserAttributes(_ arguments: [String: Any]) { + ClickstreamAnalytics.addUserAttributes(getClickstreamAttributes(arguments)) + } + + func addGlobalAttributes(_ arguments: [String: Any]) { + ClickstreamAnalytics.addGlobalAttributes(getClickstreamAttributes(arguments)) + } + + func deleteGlobalAttributes(_ arguments: [String: Any]) { + let attributes = arguments["attributes"] as! [String] + for attribute in attributes { + ClickstreamAnalytics.deleteGlobalAttributes(attribute) + } + } + + func updateConfigure(_ arguments: [String: Any]) { + do { + let configure = try ClickstreamAnalytics.getClickstreamConfiguration() + if let appId = arguments["appId"] as? String { + configure.appId = appId + } + if let endpoint = arguments["endpoint"] as? String { + configure.endpoint = endpoint + } + if let isLogEvents = arguments["isLogEvents"] as? Bool { + configure.isLogEvents = isLogEvents + } + if let isTrackScreenViewEvents = arguments["isTrackScreenViewEvents"] as? Bool { + configure.isTrackScreenViewEvents = isTrackScreenViewEvents + } + if let isTrackUserEngagementEvents = arguments["isTrackUserEngagementEvents"] as? Bool { + configure.isTrackUserEngagementEvents = isTrackUserEngagementEvents + } + if let isTrackAppExceptionEvents = arguments["isTrackAppExceptionEvents"] as? Bool { + configure.isTrackAppExceptionEvents = isTrackAppExceptionEvents + } + if let sessionTimeoutDuration = arguments["sessionTimeoutDuration"] as? Int64 { + configure.sessionTimeoutDuration = sessionTimeoutDuration + } + if let isCompressEvents = arguments["isCompressEvents"] as? Bool { + configure.isCompressEvents = isCompressEvents + } + if let authCookie = arguments["authCookie"] as? String { + configure.authCookie = authCookie + } + } catch { + log.error("Failed to config ClickstreamAnalytics: \(error)") + } + } + + func getClickstreamAttributes(_ attrs: [String: Any]) -> ClickstreamAttribute { + var attributes: ClickstreamAttribute = [:] + for (key, value) in attrs { + if value is String { + attributes[key] = value as! String + } else if value is NSNumber { + let value = value as! NSNumber + let objCType = String(cString: value.objCType) + if objCType == "c" { + attributes[key] = value.boolValue + } else if objCType == "d" { + attributes[key] = value.doubleValue + } else if objCType == "i" { + attributes[key] = value.intValue + } else if objCType == "q" { + attributes[key] = value.int64Value + } + } + } + return attributes + } } + +extension ClickstreamFlutterPlugin: DefaultLogger {} diff --git a/ios/Clickstream b/ios/Clickstream new file mode 160000 index 0000000..87f3837 --- /dev/null +++ b/ios/Clickstream @@ -0,0 +1 @@ +Subproject commit 87f38370f35d5df19014a5fbbefbbf4c6d4975c6 diff --git a/ios/amplifyconfiguration.json b/ios/amplifyconfiguration.json new file mode 100644 index 0000000..c1f3658 --- /dev/null +++ b/ios/amplifyconfiguration.json @@ -0,0 +1,15 @@ +{ + "UserAgent": "aws-solution/clickstream", + "Version": "1.0", + "analytics": { + "plugins": { + "awsClickstreamPlugin": { + "appId": "", + "endpoint": "", + "isCompressEvents": true, + "autoFlushEventsInterval": 10000, + "isTrackAppExceptionEvents": false + } + } + } +} \ No newline at end of file diff --git a/ios/clickstream_flutter.podspec b/ios/clickstream_flutter.podspec index bfa8906..fc31316 100644 --- a/ios/clickstream_flutter.podspec +++ b/ios/clickstream_flutter.podspec @@ -15,9 +15,16 @@ clickstream flutter SDK s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.platform = :ios, '11.0' + s.platform = :ios, '13.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' + s.swift_version = '5.7' + + s.subspec 'Clickstream' do |sc| + sc.source_files = 'Clickstream/Sources/**/*' + sc.dependency 'GzipSwift', '5.1.1' + sc.dependency 'Amplify', '1.30.3' + sc.dependency 'SQLite.swift', '0.13.2' + end end diff --git a/lib/clickstream_flutter.dart b/lib/clickstream_flutter.dart index 3fcb72e..8e25712 100644 --- a/lib/clickstream_flutter.dart +++ b/lib/clickstream_flutter.dart @@ -1,15 +1,85 @@ import 'clickstream_flutter_platform_interface.dart'; -class ClickstreamFlutter { - Future getPlatformVersion() { - return ClickstreamAnalytics.instance.getPlatformVersion(); +class ClickstreamAnalytics { + Future init({ + required String appId, + required String endpoint, + bool isLogEvents = false, + bool isCompressEvents = true, + bool isTrackScreenViewEvents = true, + bool isTrackUserEngagementEvents = true, + bool isTrackAppExceptionEvents = false, + int sendEventsInterval = 10000, + int sessionTimeoutDuration = 1800000, + String authCookie = "", + }) { + Map initConfig = { + 'appId': appId, + 'endpoint': endpoint, + 'isLogEvents': isLogEvents, + 'isCompressEvents': isCompressEvents, + 'isTrackScreenViewEvents': isTrackScreenViewEvents, + 'isTrackUserEngagementEvents': isTrackUserEngagementEvents, + 'isTrackAppExceptionEvents': isTrackAppExceptionEvents, + 'sendEventsInterval': sendEventsInterval, + 'sessionTimeoutDuration': sessionTimeoutDuration, + 'authCookie': authCookie + }; + return ClickstreamInterface.instance.init(initConfig); } - Future init() { - return ClickstreamAnalytics.instance.init(); + Future record( + {required String name, Map? attributes}) { + return ClickstreamInterface.instance + .record({"eventName": name, "attributes": attributes ?? {}}); } - Future record(String eventName) { - return ClickstreamAnalytics.instance.record(eventName); + Future setUserId(String? userId) { + return ClickstreamInterface.instance.setUserId({"userId": userId}); + } + + Future setUserAttributes(Map attributes) { + if (attributes.isEmpty) return Future.value(); + return ClickstreamInterface.instance.setUserAttributes(attributes); + } + + Future addGlobalAttributes(Map attributes) { + if (attributes.isEmpty) return Future.value(); + return ClickstreamInterface.instance.addGlobalAttributes(attributes); + } + + Future deleteGlobalAttributes(List attributes) { + if (attributes.isEmpty) return Future.value(); + return ClickstreamInterface.instance + .deleteGlobalAttributes({"attributes": attributes}); + } + + Future updateConfigure({ + String? appId, + String? endpoint, + bool? isLogEvents, + bool? isCompressEvents, + bool? isTrackScreenViewEvents, + bool? isTrackUserEngagementEvents, + bool? isTrackAppExceptionEvents, + int? sessionTimeoutDuration, + String? authCookie, + }) { + Map configure = { + 'appId': appId, + 'endpoint': endpoint, + 'isLogEvents': isLogEvents, + 'isCompressEvents': isCompressEvents, + 'isTrackScreenViewEvents': isTrackScreenViewEvents, + 'isTrackUserEngagementEvents': isTrackUserEngagementEvents, + 'isTrackAppExceptionEvents': isTrackAppExceptionEvents, + 'sessionTimeoutDuration': sessionTimeoutDuration, + 'authCookie': authCookie + }; + return ClickstreamInterface.instance.updateConfigure(configure); + } + + Future flushEvents() { + return ClickstreamInterface.instance.flushEvents(); } } diff --git a/lib/clickstream_flutter_method_channel.dart b/lib/clickstream_flutter_method_channel.dart index fa650b7..f342067 100644 --- a/lib/clickstream_flutter_method_channel.dart +++ b/lib/clickstream_flutter_method_channel.dart @@ -4,27 +4,49 @@ import 'package:flutter/services.dart'; import 'clickstream_flutter_platform_interface.dart'; /// An implementation of [ClickstreamFlutterPlatform] that uses method channels. -class MethodChannelClickstreamAnalytics extends ClickstreamAnalytics { +class ClickstreamAnalyticsMethodChannel extends ClickstreamInterface { /// The method channel used to interact with the native platform. @visibleForTesting final methodChannel = const MethodChannel('clickstream_flutter'); @override - Future getPlatformVersion() async { - final version = - await methodChannel.invokeMethod('getPlatformVersion'); - return version; + Future init(Map configure) async { + final result = await methodChannel.invokeMethod('init', configure); + return result ?? false; } @override - Future init() async { - final result = await methodChannel.invokeMethod('init'); - return result ?? false; + Future record(Map attributes) async { + await methodChannel.invokeMethod('record', attributes); } @override - Future record(String eventName) async { - final result = await methodChannel.invokeMethod('record', eventName); - return result ?? false; + Future setUserId(Map userId) async { + await methodChannel.invokeMethod('setUserId', userId); + } + + @override + Future setUserAttributes(Map attributes) async { + await methodChannel.invokeMethod('setUserAttributes', attributes); + } + + @override + Future addGlobalAttributes(Map attributes) async { + await methodChannel.invokeMethod('addGlobalAttributes', attributes); + } + + @override + Future deleteGlobalAttributes(Map attributes) async { + await methodChannel.invokeMethod('deleteGlobalAttributes', attributes); + } + + @override + Future updateConfigure(Map configure) async { + await methodChannel.invokeMethod('updateConfigure', configure); + } + + @override + Future flushEvents() async { + await methodChannel.invokeMethod('flushEvents'); } } diff --git a/lib/clickstream_flutter_platform_interface.dart b/lib/clickstream_flutter_platform_interface.dart index 3efaae2..de9ddb5 100644 --- a/lib/clickstream_flutter_platform_interface.dart +++ b/lib/clickstream_flutter_platform_interface.dart @@ -2,36 +2,56 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'clickstream_flutter_method_channel.dart'; -abstract class ClickstreamAnalytics extends PlatformInterface { +abstract class ClickstreamInterface extends PlatformInterface { /// Constructs a ClickstreamAnalytics. - ClickstreamAnalytics() : super(token: _token); + ClickstreamInterface() : super(token: _token); static final Object _token = Object(); - static ClickstreamAnalytics _instance = MethodChannelClickstreamAnalytics(); + static ClickstreamInterface _instance = ClickstreamAnalyticsMethodChannel(); - /// The default instance of [ClickstreamAnalytics] to use. + /// The default instance of [ClickstreamInterface] to use. /// - /// Defaults to [MethodChannelClickstreamAnalytics]. - static ClickstreamAnalytics get instance => _instance; + /// Defaults to [ClickstreamAnalyticsMethodChannel]. + static ClickstreamInterface get instance => _instance; /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [ClickstreamAnalytics] when + /// platform-specific class that extends [ClickstreamInterface] when /// they register themselves. - static set instance(ClickstreamAnalytics instance) { + static set instance(ClickstreamInterface instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); + Future init(Map configure) { + throw UnimplementedError('init() has not been implemented.'); } - Future init() { - throw UnimplementedError('init() has not been implemented.'); + Future record(Map attributes) { + throw UnimplementedError('record() has not been implemented.'); + } + + Future setUserId(Map userId) { + throw UnimplementedError('setUserId() has not been implemented.'); + } + + Future setUserAttributes(Map attributes) { + throw UnimplementedError('setUserId() has not been implemented.'); + } + + Future addGlobalAttributes(Map attributes) { + throw UnimplementedError('setUserId() has not been implemented.'); + } + + Future deleteGlobalAttributes(Map attributes) { + throw UnimplementedError('setUserId() has not been implemented.'); + } + + Future updateConfigure(Map configure) { + throw UnimplementedError('setUserId() has not been implemented.'); } - Future record(String eventName) { - throw UnimplementedError('platformVersion() has not been implemented.'); + Future flushEvents() { + throw UnimplementedError('setUserId() has not been implemented.'); } } diff --git a/test/clickstream_flutter_method_channel_test.dart b/test/clickstream_flutter_method_channel_test.dart index a247419..0ecc7e8 100644 --- a/test/clickstream_flutter_method_channel_test.dart +++ b/test/clickstream_flutter_method_channel_test.dart @@ -5,23 +5,115 @@ import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - MethodChannelClickstreamAnalytics platform = MethodChannelClickstreamAnalytics(); + ClickstreamAnalyticsMethodChannel platform = + ClickstreamAnalyticsMethodChannel(); const MethodChannel channel = MethodChannel('clickstream_flutter'); setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( channel, (MethodCall methodCall) async { - return '42'; + switch (methodCall.method) { + case "init": + if (methodCall.arguments['endpoint'] == "") { + return false; + } else { + return true; + } + case "record": + return null; + case "setUserId": + return null; + case "setUserAttributes": + return null; + case "setGlobalAttributes": + return null; + case "deleteGlobalAttributes": + return null; + case "updateConfigure": + return null; + case "flushEvents": + return null; + } }, ); }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); }); - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); + test('init failed', () async { + Map initConfig = { + 'appId': 'testApp', + 'endpoint': "", + }; + expect(await platform.init(initConfig), false); + }); + + test('init success', () async { + Map initConfig = { + 'appId': 'testApp', + 'endpoint': "http://example.com/collect", + }; + expect(await platform.init(initConfig), true); + }); + + test('record', () async { + Map attributes = { + "category": "shoes", + "currency": "CNY", + "value": 279.9 + }; + var result = platform.record(attributes); + expect(result, isNotNull); + }); + + test('setUserId', () async { + Map attributes = { + "userId": "1234", + }; + var result = platform.setUserId(attributes); + expect(result, isNotNull); + }); + + test('setUserAttributes', () async { + Map attributes = {"_user_age": 21, "_user_name": "carl"}; + var result = platform.setUserAttributes(attributes); + expect(result, isNotNull); + }); + + test('setGlobalAttributes', () async { + Map attributes = { + "channel": "Play Store", + "level": 5.1, + "class": 6 + }; + var result = platform.addGlobalAttributes(attributes); + expect(result, isNotNull); + }); + + test('deleteGlobalAttributes', () async { + Map attributes = { + "attributes": ["attr1", "attr2"], + }; + var result = platform.deleteGlobalAttributes(attributes); + expect(result, isNotNull); + }); + + test('updateConfigure', () async { + Map attributes = { + "appId": "newAppId", + "endpoint": "https://example.com/collect", + }; + var result = platform.updateConfigure(attributes); + expect(result, isNotNull); + }); + + test('setGlobalAttributes', () async { + var result = platform.flushEvents(); + expect(result, isNotNull); }); } diff --git a/test/clickstream_flutter_test.dart b/test/clickstream_flutter_test.dart index d958863..b2df4df 100644 --- a/test/clickstream_flutter_test.dart +++ b/test/clickstream_flutter_test.dart @@ -1,35 +1,117 @@ -import 'package:flutter_test/flutter_test.dart'; import 'package:clickstream_flutter/clickstream_flutter.dart'; -import 'package:clickstream_flutter/clickstream_flutter_platform_interface.dart'; import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; +import 'package:clickstream_flutter/clickstream_flutter_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockClickstreamFlutterPlatform with MockPlatformInterfaceMixin - implements ClickstreamAnalytics { + implements ClickstreamInterface { @override - Future getPlatformVersion() => Future.value('42'); + Future init(Map configure) => Future.value(true); @override - Future init() => Future.value(true); + Future record(Map params) => Future.value(); @override - Future record(String name) => Future.value(); + Future flushEvents() => Future.value(); + + @override + Future addGlobalAttributes(Map attributes) => + Future.value(); + + @override + Future setUserAttributes(Map attributes) => + Future.value(); + + @override + Future setUserId(Map userId) => Future.value(); + + @override + Future updateConfigure(Map configure) => + Future.value(); + + @override + Future deleteGlobalAttributes(Map attributes) => + Future.value(); } void main() { - final ClickstreamAnalytics initialPlatform = ClickstreamAnalytics.instance; - - test('$MethodChannelClickstreamAnalytics is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); + final ClickstreamInterface initialPlatform = ClickstreamInterface.instance; + late ClickstreamAnalytics analytics; - test('getPlatformVersion', () async { - ClickstreamFlutter clickstreamFlutterPlugin = ClickstreamFlutter(); + setUp(() { + analytics = ClickstreamAnalytics(); MockClickstreamFlutterPlatform fakePlatform = MockClickstreamFlutterPlatform(); - ClickstreamAnalytics.instance = fakePlatform; + ClickstreamInterface.instance = fakePlatform; + }); + + test('$ClickstreamAnalyticsMethodChannel is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('initSDK', () async { + var result = await analytics.init( + appId: 'testApp', endpoint: "https://example.com/collect"); + expect(result, true); + }); + + test('record event', () async { + var result = analytics.record(name: "testEvent"); + expect(result, isNotNull); + }); + + test('record event with attributes', () async { + var result = analytics.record( + name: "testEvent", + attributes: {"category": "shoes", "currency": "CNY", "value": 279.9}); + expect(result, isNotNull); + }); + test('setUserId', () async { + var result = analytics.setUserId("11234"); + expect(result, isNotNull); + }); + + test('setUserAttributes', () async { + var result = + analytics.setUserAttributes({"_user_age": 21, "_user_name": "carl"}); + var result1 = analytics.setUserAttributes({}); + expect(result, isNotNull); + expect(result1, isNotNull); + }); + + test('setGlobalAttributes', () async { + var result = analytics.addGlobalAttributes( + {"channel": "Play Store", "level": 5.1, "class": 6}); + var result1 = analytics.addGlobalAttributes({}); + expect(result, isNotNull); + expect(result1, isNotNull); + }); + + test('deleteGlobalAttributes', () async { + var result = analytics.deleteGlobalAttributes(["attr1", "attr2"]); + var result1 = analytics.deleteGlobalAttributes([]); + expect(result, isNotNull); + expect(result1, isNotNull); + }); + + test('updateConfigure', () async { + var result = analytics.updateConfigure( + appId: "testApp1", + endpoint: "https://example.com/collect", + isLogEvents: true, + isCompressEvents: false, + isTrackScreenViewEvents: false, + isTrackUserEngagementEvents: false, + isTrackAppExceptionEvents: false, + sessionTimeoutDuration: 18000, + authCookie: "your auth cookie"); + expect(result, isNotNull); + }); - expect(await clickstreamFlutterPlugin.getPlatformVersion(), '42'); + test('flushEvents', () async { + var result = analytics.flushEvents(); + expect(result, isNotNull); }); } From e7aa17747d213670cae997d182e8d6dc08189aae Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Wed, 25 Oct 2023 15:02:57 +0800 Subject: [PATCH 4/5] feat: add readme --- .gitignore | 1 + .pubignore | 9 + CHANGELOG.md | 4 +- README.md | 188 +++++++++++++++++- android/build.gradle | 4 +- android/settings.gradle | 2 +- android/src/main/AndroidManifest.xml | 2 +- .../ClickstreamFlutterPlugin.kt | 16 +- .../ClickstreamFlutterPluginTest.kt | 2 +- .../plugin_integration_test.dart | 12 +- example/ios/Podfile.lock | 14 +- example/lib/main.dart | 45 ++--- example/pubspec.lock | 4 +- example/pubspec.yaml | 2 +- example/test/widget_test.dart | 4 +- ios/Assets/.gitkeep | 0 ios/Assets/amplifyconfiguration.json | 15 -- ios/Classes/ClickstreamFlutterPlugin.swift | 7 + ios/amplifyconfiguration.json | 15 -- ....podspec => clickstream_analytics.podspec} | 8 +- ...lutter.dart => clickstream_analytics.dart} | 5 +- ...clickstream_analytics_method_channel.dart} | 8 +- ...kstream_analytics_platform_interface.dart} | 5 +- pubspec.yaml | 10 +- ...ickstream_flutter_method_channel_test.dart | 6 +- test/clickstream_flutter_test.dart | 9 +- 26 files changed, 283 insertions(+), 114 deletions(-) create mode 100644 .pubignore rename android/src/main/kotlin/software/aws/solution/{clickstream_flutter => clickstream_analytics}/ClickstreamFlutterPlugin.kt (94%) rename android/src/test/kotlin/software/aws/solution/{clickstream_flutter => clickstream_analytics}/ClickstreamFlutterPluginTest.kt (94%) delete mode 100644 ios/Assets/.gitkeep delete mode 100644 ios/Assets/amplifyconfiguration.json delete mode 100644 ios/amplifyconfiguration.json rename ios/{clickstream_flutter.podspec => clickstream_analytics.podspec} (74%) rename lib/{clickstream_flutter.dart => clickstream_analytics.dart} (94%) rename lib/{clickstream_flutter_method_channel.dart => clickstream_analytics_method_channel.dart} (86%) rename lib/{clickstream_flutter_platform_interface.dart => clickstream_analytics_platform_interface.dart} (92%) diff --git a/.gitignore b/.gitignore index 96486fd..383c7b1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ migrate_working_dir/ .dart_tool/ .packages build/ +coverage/ diff --git a/.pubignore b/.pubignore new file mode 100644 index 0000000..b88d1de --- /dev/null +++ b/.pubignore @@ -0,0 +1,9 @@ +*.iml +test +coverage +build +android/src/test +ios/Clickstream/* +!ios/Clickstream/Sources +example/integration_test +example/test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 41cc7d8..8be37fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 0.1.0 -* TODO: Describe initial release. +* feat: add basic android and swift SDK APIs. diff --git a/README.md b/README.md index 2b94624..fcf130d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,182 @@ -# clickstream_flutter +# AWS Solution Clickstream Analytics SDK for Flutter -clickstream flutter SDK +## Introduction -## Getting Started +Clickstream Flutter SDK can help you easily collect and report events from browser to AWS. This SDK is part of an AWS solution - [Clickstream Analytics on AWS](https://github.com/awslabs/clickstream-analytics-on-aws), which provisions data pipeline to ingest and process event data into AWS services such as S3, Redshift. -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +The SDK relies on the [Clickstream Android SDK](https://github.com/awslabs/clickstream-android) and [Clickstream Swift SDK](https://github.com/awslabs/clickstream-swift). Therefore, flutter SDK also supports automatically collect common user events and attributes (e.g., session start, first open) In addition, we've added easy-to-use APIs to simplify data collection in Flutter apps. -For help getting started with Flutter development, view the -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. \ No newline at end of file +## Integrate SDK + +### Include SDK + +```bash +flutter pub add clickstream_analytics +``` + +After complete, rebuild your Flutter application: + +```bash +flutter run +``` + +### Initialize the SDK + +Copy your configuration code from your clickstream solution web console, the configuration code should look like as follows. You can also manually add this code snippet and replace the values of appId and endpoint after you registered app to a data pipeline in the Clickstream Analytics solution console. + +```dart +import 'package:clickstream_analytics/clickstream_analytics.dart'; + +final analytics = ClickstreamAnalytics(); +analytics.init({ + appId: "your appId", + endpoint: "https://example.com/collect" +}); +``` + +Please noteļ¼š + +1. Your `appId` and `endpoint` are already set up in it. +2. We only need to initialize the SDK once after the application starts. It is recommended to do it in the main function of your App. +3. We can use `bool result = await analytics.init()` to get the boolean value of the initialization result. + +### Start using + +#### Record event + +Add the following code where you need to record event. + +```dart +import 'package:clickstream_analytics/clickstream_analytics.dart'; + +final analytics = ClickstreamAnalytics(); + +// record event with attributes +analytics.record(name: 'button_click', attributes: { + "event_category": "shoes", + "currency": "CNY", + "value": 279.9 +}); + +//record event with name +analytics.record(name: "button_click"); +``` + +#### Login and logout + +```dart +/// when user login success. +analytics.setUserId("userId"); + +/// when user logout +analytics.setUserId(null); +``` + +#### Add user attribute + +```dart +analytics.setUserAttributes({ + "userName":"carl", + "userAge": 22 +}); +``` + +When opening for the first time after integrating the SDK, you need to manually set the user attributes once, and current login user's attributes will be cached in native disk, so the next time browser open you don't need to set all user's attribute again, of course you can use the same api `analytics.setUserAttributes()` to update the current user's attribute when it changes. + +#### Add global attribute + +```dart +analytics.addGlobalAttributes({ + "_traffic_source_medium": "Search engine", + "_traffic_source_name": "Summer promotion", + "level": 10 +}); +``` + +It is recommended to set global attributes after each SDK initialization, global attributes will be included in all events that occur after it is set. + +#### Other configurations + +In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage: + +```dart +final analytics = ClickstreamAnalytics(); +analytics.init( + appId: "your appId", + endpoint: "https://example.com/collect", + isLogEvents: false, + isCompressEvents: false, + sendEventsInterval: 5000, + isTrackScreenViewEvents: true, + isTrackUserEngagementEvents: true, + isTrackAppExceptionEvents: false, + authCookie: "your auth cookie", + sessionTimeoutDuration: 1800000 +); +``` + +Here is an explanation of each property: + +- **appId (Required)**: the app id of your project in control plane. +- **endpoint (Required)**: the endpoint path you will upload the event to AWS server. +- **isLogEvents**: whether to print out event json for debugging, default is false. +- **isCompressEvents**: whether to compress event content when uploading events, default is `true` +- **sendEventsInterval**: event sending interval millisecond, works only bath send mode, the default value is `5000` +- **isTrackScreenViewEvents**: whether auto record screen view events in app, default is `true` +- **isTrackUserEngagementEvents**: whether auto record user engagement events in app, default is `true` +- **isTrackAppExceptionEvents**: whether auto track exception event in app, default is `false` +- **authCookie**: your auth cookie for AWS application load balancer auth cookie. +- **sessionTimeoutDuration**: the duration for session timeout millisecond, default is 1800000 + +#### Configuration update + +You can update the default configuration after initializing the SDK, below are the additional configuration options you can customize. + +```dart +final analytics = ClickstreamAnalytics(); +analytics.updateConfigure( + appId: "your appId", + endpoint: "https://example.com/collect", + isLogEvents: true, + isCompressEvents: false, + isTrackScreenViewEvents: false + isTrackUserEngagementEvents: false, + isTrackAppExceptionEvents: false, + sessionTimeoutDuration: 100000, + authCookie: "test cookie"); +``` + +#### Send event immediately + +```dart +final analytics = ClickstreamAnalytics(); +analytics.flushEvents(); +``` + +## How to build and test locally + +### Build + +```bash +flutter pub get +``` + +### Format and lint + +```bash +dart format . && flutter analyze +``` + +### Test + +```bash +flutter test +``` + +## Security + +See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. + +## License + +This library is licensed under the [Apache 2.0 License](./LICENSE). diff --git a/android/build.gradle b/android/build.gradle index 40e7772..9a51158 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,4 +1,4 @@ -group 'software.aws.solution.clickstream_flutter' +group 'software.aws.solution.clickstream_analytics' version '1.0-SNAPSHOT' buildscript { @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' android { if (project.android.hasProperty("namespace")) { - namespace 'software.aws.solution.clickstream_flutter' + namespace 'software.aws.solution.clickstream_analytics' } compileSdkVersion 33 diff --git a/android/settings.gradle b/android/settings.gradle index 4613fad..6b7d429 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'clickstream_flutter' +rootProject.name = 'clickstream_analytics' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index f5376d0..936f8cf 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="software.aws.solution.clickstream_analytics"> diff --git a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt b/android/src/main/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPlugin.kt similarity index 94% rename from android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt rename to android/src/main/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPlugin.kt index 1adc22b..9390d02 100644 --- a/android/src/main/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPlugin.kt +++ b/android/src/main/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPlugin.kt @@ -1,4 +1,18 @@ -package software.aws.solution.clickstream_flutter +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package software.aws.solution.clickstream_analytics import android.app.Activity import com.amazonaws.logging.Log diff --git a/android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt b/android/src/test/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPluginTest.kt similarity index 94% rename from android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt rename to android/src/test/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPluginTest.kt index 9f69015..70b2b44 100644 --- a/android/src/test/kotlin/software/aws/solution/clickstream_flutter/ClickstreamFlutterPluginTest.kt +++ b/android/src/test/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPluginTest.kt @@ -1,4 +1,4 @@ -package software.aws.solution.clickstream_flutter +package software.aws.solution.clickstream_analytics import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel diff --git a/example/integration_test/plugin_integration_test.dart b/example/integration_test/plugin_integration_test.dart index 953f468..f9b8a92 100644 --- a/example/integration_test/plugin_integration_test.dart +++ b/example/integration_test/plugin_integration_test.dart @@ -1,16 +1,10 @@ -// This is a basic Flutter integration test. -// -// Since integration tests run in a full Flutter application, they can interact -// with the host side of a plugin implementation, unlike Dart unit tests. -// -// For more information about Flutter integration tests, please see -// https://docs.flutter.dev/cookbook/testing/integration/introduction - +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:clickstream_flutter/clickstream_flutter.dart'; +import 'package:clickstream_analytics/clickstream_analytics.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e74f0d5..3388114 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2,10 +2,10 @@ PODS: - Amplify (1.30.3): - Amplify/Default (= 1.30.3) - Amplify/Default (1.30.3) - - clickstream_flutter (0.0.1): - - clickstream_flutter/Clickstream (= 0.0.1) + - clickstream_analytics (0.0.1): + - clickstream_analytics/Clickstream (= 0.0.1) - Flutter - - clickstream_flutter/Clickstream (0.0.1): + - clickstream_analytics/Clickstream (0.0.1): - Amplify (= 1.30.3) - Flutter - GzipSwift (= 5.1.1) @@ -19,7 +19,7 @@ PODS: - SQLite.swift/standard (0.13.2) DEPENDENCIES: - - clickstream_flutter (from `.symlinks/plugins/clickstream_flutter/ios`) + - clickstream_analytics (from `.symlinks/plugins/clickstream_analytics/ios`) - Flutter (from `Flutter`) - integration_test (from `.symlinks/plugins/integration_test/ios`) @@ -30,8 +30,8 @@ SPEC REPOS: - SQLite.swift EXTERNAL SOURCES: - clickstream_flutter: - :path: ".symlinks/plugins/clickstream_flutter/ios" + clickstream_analytics: + :path: ".symlinks/plugins/clickstream_analytics/ios" Flutter: :path: Flutter integration_test: @@ -39,7 +39,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Amplify: 516e5da5f256f62841b6bc659e1644bc999d7b6e - clickstream_flutter: d5dd3ad04cb641b27c1014c23b4cdbc98f6956dc + clickstream_analytics: 22312ed8d353813f1f6847aecb4e04b62bdda478 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa integration_test: 13825b8a9334a850581300559b8839134b124670 diff --git a/example/lib/main.dart b/example/lib/main.dart index 22777b5..547e002 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,9 @@ -import 'package:flutter/material.dart'; -import 'dart:async'; +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 -import 'package:flutter/services.dart'; -import 'package:clickstream_flutter/clickstream_flutter.dart'; +import 'package:clickstream_analytics/clickstream_analytics.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); @@ -22,24 +23,12 @@ class _MyAppState extends State { @override void initState() { super.initState(); - initPlatformState(); } - // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String platformVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - // We also handle the message potentially returning null. - try { - platformVersion = 'Unknown platform version'; - } on PlatformException { - platformVersion = 'Failed to get platform version.'; + void log(String message) { + if (kDebugMode) { + print(message); } - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; } @override @@ -55,12 +44,12 @@ class _MyAppState extends State { leading: const Icon(Icons.not_started_outlined), title: const Text('initSDK'), onTap: () async { - var result = await analytics.init( + bool result = await analytics.init( appId: "shopping", endpoint: testEndpoint, isLogEvents: true, isCompressEvents: false); - print("init SDK result is:$result"); + log("init SDK result is:$result"); }, minLeadingWidth: 0, ), @@ -78,7 +67,7 @@ class _MyAppState extends State { "boolValue": true, "value": 279.9 }); - print("recorded testEvent and testEventWithName"); + log("recorded testEvent and testEventWithName"); }, minLeadingWidth: 0, ), @@ -88,7 +77,7 @@ class _MyAppState extends State { onTap: () async { analytics.setUserId("12345"); analytics.setUserId(null); - print("setUserId"); + log("setUserId"); }, minLeadingWidth: 0, ), @@ -100,7 +89,7 @@ class _MyAppState extends State { {"category": 'shoes', "currency": 'CNY', "value": 279.9}); analytics.setUserAttributes({}); analytics.setUserAttributes({"testNull": null}); - print("setUserAttributes"); + log("setUserAttributes"); }, minLeadingWidth: 0, ), @@ -114,7 +103,7 @@ class _MyAppState extends State { "isTrue": true, "Score": 24.32 }); - print("addGlobalAttributes"); + log("addGlobalAttributes"); }, minLeadingWidth: 0, ), @@ -123,7 +112,7 @@ class _MyAppState extends State { title: const Text('deleteGlobalAttributes'), onTap: () async { analytics.deleteGlobalAttributes(["Score", "_channel"]); - print("deleteGlobalAttributes Score and _channel"); + log("deleteGlobalAttributes Score and _channel"); }, minLeadingWidth: 0, ), @@ -140,7 +129,7 @@ class _MyAppState extends State { authCookie: "test cookie", isTrackScreenViewEvents: false); analytics.updateConfigure(); - print("updateConfigure"); + log("updateConfigure"); }, minLeadingWidth: 0, ), @@ -149,7 +138,7 @@ class _MyAppState extends State { title: const Text('flushEvents'), onTap: () async { analytics.flushEvents(); - print("flushEvents"); + log("flushEvents"); }, minLeadingWidth: 0, ), diff --git a/example/pubspec.lock b/example/pubspec.lock index ff94088..092add3 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -25,13 +25,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - clickstream_flutter: + clickstream_analytics: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.0.1" + version: "0.1.0" clock: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 2cbc787..3305679 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter: sdk: flutter - clickstream_flutter: + clickstream_analytics: # When depending on this package from a real application you should use: # clickstream_flutter: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index a56ab1a..8721e25 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -18,8 +18,8 @@ void main() { // Verify that platform version is retrieved. expect( find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ios/Assets/amplifyconfiguration.json b/ios/Assets/amplifyconfiguration.json deleted file mode 100644 index c1f3658..0000000 --- a/ios/Assets/amplifyconfiguration.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "UserAgent": "aws-solution/clickstream", - "Version": "1.0", - "analytics": { - "plugins": { - "awsClickstreamPlugin": { - "appId": "", - "endpoint": "", - "isCompressEvents": true, - "autoFlushEventsInterval": 10000, - "isTrackAppExceptionEvents": false - } - } - } -} \ No newline at end of file diff --git a/ios/Classes/ClickstreamFlutterPlugin.swift b/ios/Classes/ClickstreamFlutterPlugin.swift index f78a6a6..0e6613e 100644 --- a/ios/Classes/ClickstreamFlutterPlugin.swift +++ b/ios/Classes/ClickstreamFlutterPlugin.swift @@ -1,3 +1,10 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + import Amplify import Flutter import UIKit diff --git a/ios/amplifyconfiguration.json b/ios/amplifyconfiguration.json deleted file mode 100644 index c1f3658..0000000 --- a/ios/amplifyconfiguration.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "UserAgent": "aws-solution/clickstream", - "Version": "1.0", - "analytics": { - "plugins": { - "awsClickstreamPlugin": { - "appId": "", - "endpoint": "", - "isCompressEvents": true, - "autoFlushEventsInterval": 10000, - "isTrackAppExceptionEvents": false - } - } - } -} \ No newline at end of file diff --git a/ios/clickstream_flutter.podspec b/ios/clickstream_analytics.podspec similarity index 74% rename from ios/clickstream_flutter.podspec rename to ios/clickstream_analytics.podspec index fc31316..b2cc817 100644 --- a/ios/clickstream_flutter.podspec +++ b/ios/clickstream_analytics.podspec @@ -1,17 +1,17 @@ # # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint clickstream_flutter.podspec` to validate before publishing. +# Run `pod lib lint clickstream_analytics.podspec` to validate before publishing. # Pod::Spec.new do |s| - s.name = 'clickstream_flutter' + s.name = 'clickstream_analytics' s.version = '0.0.1' s.summary = 'clickstream flutter SDK' s.description = <<-DESC clickstream flutter SDK DESC - s.homepage = 'http://example.com' + s.homepage = 'https://github.com/awslabs/clickstream-flutter' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'AWS GCR Solutions Team' => 'aws-gcr-solutions@amazon.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' diff --git a/lib/clickstream_flutter.dart b/lib/clickstream_analytics.dart similarity index 94% rename from lib/clickstream_flutter.dart rename to lib/clickstream_analytics.dart index 8e25712..6f92790 100644 --- a/lib/clickstream_flutter.dart +++ b/lib/clickstream_analytics.dart @@ -1,4 +1,7 @@ -import 'clickstream_flutter_platform_interface.dart'; +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'clickstream_analytics_platform_interface.dart'; class ClickstreamAnalytics { Future init({ diff --git a/lib/clickstream_flutter_method_channel.dart b/lib/clickstream_analytics_method_channel.dart similarity index 86% rename from lib/clickstream_flutter_method_channel.dart rename to lib/clickstream_analytics_method_channel.dart index f342067..9973ac0 100644 --- a/lib/clickstream_flutter_method_channel.dart +++ b/lib/clickstream_analytics_method_channel.dart @@ -1,7 +1,10 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'clickstream_flutter_platform_interface.dart'; +import 'clickstream_analytics_platform_interface.dart'; /// An implementation of [ClickstreamFlutterPlatform] that uses method channels. class ClickstreamAnalyticsMethodChannel extends ClickstreamInterface { @@ -37,7 +40,8 @@ class ClickstreamAnalyticsMethodChannel extends ClickstreamInterface { @override Future deleteGlobalAttributes(Map attributes) async { - await methodChannel.invokeMethod('deleteGlobalAttributes', attributes); + await methodChannel.invokeMethod( + 'deleteGlobalAttributes', attributes); } @override diff --git a/lib/clickstream_flutter_platform_interface.dart b/lib/clickstream_analytics_platform_interface.dart similarity index 92% rename from lib/clickstream_flutter_platform_interface.dart rename to lib/clickstream_analytics_platform_interface.dart index de9ddb5..68edaf6 100644 --- a/lib/clickstream_flutter_platform_interface.dart +++ b/lib/clickstream_analytics_platform_interface.dart @@ -1,6 +1,9 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'clickstream_flutter_method_channel.dart'; +import 'clickstream_analytics_method_channel.dart'; abstract class ClickstreamInterface extends PlatformInterface { /// Constructs a ClickstreamAnalytics. diff --git a/pubspec.yaml b/pubspec.yaml index f64e36e..cc8935d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ -name: clickstream_flutter -description: clickstream flutter SDK -version: 0.0.1 -homepage: +name: clickstream_analytics +description: AWS Solution Clickstream Analytics SDK for Flutter +version: 0.1.0 +homepage: https://github.com/awslabs/clickstream-flutter environment: sdk: '>=3.1.3 <4.0.0' @@ -35,7 +35,7 @@ flutter: plugin: platforms: android: - package: software.aws.solution.clickstream_flutter + package: software.aws.solution.clickstream_analytics pluginClass: ClickstreamFlutterPlugin ios: pluginClass: ClickstreamFlutterPlugin diff --git a/test/clickstream_flutter_method_channel_test.dart b/test/clickstream_flutter_method_channel_test.dart index 0ecc7e8..466cd81 100644 --- a/test/clickstream_flutter_method_channel_test.dart +++ b/test/clickstream_flutter_method_channel_test.dart @@ -1,6 +1,9 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; +import 'package:clickstream_analytics/clickstream_analytics_method_channel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -36,6 +39,7 @@ void main() { case "flushEvents": return null; } + return null; }, ); }); diff --git a/test/clickstream_flutter_test.dart b/test/clickstream_flutter_test.dart index b2df4df..7074dd9 100644 --- a/test/clickstream_flutter_test.dart +++ b/test/clickstream_flutter_test.dart @@ -1,6 +1,9 @@ -import 'package:clickstream_flutter/clickstream_flutter.dart'; -import 'package:clickstream_flutter/clickstream_flutter_method_channel.dart'; -import 'package:clickstream_flutter/clickstream_flutter_platform_interface.dart'; +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'package:clickstream_analytics/clickstream_analytics.dart'; +import 'package:clickstream_analytics/clickstream_analytics_method_channel.dart'; +import 'package:clickstream_analytics/clickstream_analytics_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; From 933c8d8ba48c4d6da3c63d24909a2a4d2b453af4 Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Thu, 26 Oct 2023 10:37:30 +0800 Subject: [PATCH 5/5] feat: add github actions --- .github/ISSUE_TEMPLATE/bug_report.yaml | 88 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yaml | 32 ++++++++ .github/PULL_REQUEST_TEMPLATE.md | 16 ++++ .github/release.yml | 11 +++ .github/workflows/build-android.yml | 25 ++++++ .github/workflows/build-ios.yml | 25 ++++++ .github/workflows/code-lint.yml | 23 ++++++ .github/workflows/release.yml | 38 +++++++++ .github/workflows/test.yml | 25 ++++++ .github/workflows/title-lint.yml | 24 ++++++ README.md | 22 +++++- example/README.md | 2 +- release.sh | 7 ++ 13 files changed, 333 insertions(+), 5 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yaml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/release.yml create mode 100644 .github/workflows/build-android.yml create mode 100644 .github/workflows/build-ios.yml create mode 100644 .github/workflows/code-lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/title-lint.yml create mode 100755 release.sh diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..6af5821 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,88 @@ +name: Bug Report +description: Create a report to help us improve +body: + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + id: repro + attributes: + label: Steps To Reproduce + description: How do you trigger this bug? Please walk us through it step by step. + value: | + Steps to reproduce the behavior: + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + render: dart + validations: + required: true + - type: textarea + id: behavior + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: input + id: clickstream-version + attributes: + label: ClickstreamAnalytic Flutter SDK Version + placeholder: e.g. 1.0.0 + validations: + required: true + - type: input + id: flutter-version + attributes: + label: Flutter version + placeholder: e.g. 3.3.0 + validations: + required: true + - type: input + id: platform + attributes: + label: The platform of the bug + placeholder: | + - e.g. Android/iOS/All + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: >- + Include any relevant log output + value: | +
+ Log Messages + + ``` + INSERT LOG MESSAGES HERE + ``` +
+ render: shell + - type: dropdown + id: regression + attributes: + label: Is this a regression? + multiple: false + options: + - "Yes" + - "No" + validations: + required: true + - type: textarea + id: regression-info + attributes: + label: Regression additional context + placeholder: If it was a regression provide the versions used before and after the upgrade. + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000..36105ac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,32 @@ +name: Feature request +description: Suggest an idea for this project +body: + - type: textarea + id: description + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..5503089 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## Issue \# + + +## Description + + +## General Checklist + + +- [ ] Added new tests to cover change, if needed +- [ ] Security oriented best practices and standards are followed (e.g. using input sanitization, principle of least privilege, etc) +- [ ] Documentation update for the change if required +- [ ] PR title conforms to conventional commit style +- [ ] If breaking change, documentation/changelog update with migration instructions + +By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..50154fe --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,11 @@ +changelog: + categories: + - title: Feature + labels: + - enhancement + - title: Fix + labels: + - bug + - title: Other Changes + labels: + - "*" \ No newline at end of file diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml new file mode 100644 index 0000000..3aae12e --- /dev/null +++ b/.github/workflows/build-android.yml @@ -0,0 +1,25 @@ +name: Flutter SDK Build for Android + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '11' + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Run build apk + run: | + flutter pub get + cd example + flutter build apk diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml new file mode 100644 index 0000000..5ba82c4 --- /dev/null +++ b/.github/workflows/build-ios.yml @@ -0,0 +1,25 @@ +name: Flutter SDK Build for iOS + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-ios: + runs-on: macos-13-xl + steps: + - uses: actions/checkout@v3 + - name: Initialize submodules + run: | + git submodule update --init + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + architecture: x64 + - name: Run build ios + run: | + flutter pub get + cd example + flutter build ios --release --no-codesign diff --git a/.github/workflows/code-lint.yml b/.github/workflows/code-lint.yml new file mode 100644 index 0000000..269c06a --- /dev/null +++ b/.github/workflows/code-lint.yml @@ -0,0 +1,23 @@ +name: Flutter SDK Lint + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + code-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - run: flutter --version + - name: Run install packages + run: flutter pub get + - name: Run code format + run: dart format . --set-exit-if-changed + - name: Run code lint + run: flutter analyze --fatal-warnings diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..881c9d2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release Next Version +on: + workflow_dispatch: + inputs: + release_tag: + description: 'Release Tag' + required: true + type: string +env: + NEW_VERSION: ${{ github.event.inputs.release_tag }} +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.PROJECT_TOKEN }} + - name: Modify for next release + run: | + chmod +x release.sh + ./release.sh ${{ env.NEW_VERSION }} + git diff + git config user.name '${{ vars.USER_NAME }}' + git config user.email '${{ vars.USER_EMAIL }}' + git add . + git commit -m 'release: clickstream Flutter ${{ env.NEW_VERSION }}' + git push + git tag v${{ env.NEW_VERSION }} + git push origin v${{ env.NEW_VERSION }} + - name: Create GitHub release + uses: softprops/action-gh-release@v1 + with: + name: "Clickstream Flutter ${{ env.NEW_VERSION }}" + tag_name: "v${{ env.NEW_VERSION }}" + prerelease: true + generate_release_notes: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a7fea28 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Flutter SDK Test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + code-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Run test + run: | + flutter pub get + flutter test --coverage --reporter github + - name: Upload Test Report + uses: codecov/codecov-action@v3 + with: + name: report + files: coverage/lcov.info \ No newline at end of file diff --git a/.github/workflows/title-lint.yml b/.github/workflows/title-lint.yml new file mode 100644 index 0000000..e2e8aab --- /dev/null +++ b/.github/workflows/title-lint.yml @@ -0,0 +1,24 @@ +name: Pull Request Title Lint + +on: + pull_request: + branches: [ "*" ] + +jobs: + title-lint: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: |- + feat + fix + chore + docs + ci + tests + requireScope: false \ No newline at end of file diff --git a/README.md b/README.md index fcf130d..227213a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ## Introduction -Clickstream Flutter SDK can help you easily collect and report events from browser to AWS. This SDK is part of an AWS solution - [Clickstream Analytics on AWS](https://github.com/awslabs/clickstream-analytics-on-aws), which provisions data pipeline to ingest and process event data into AWS services such as S3, Redshift. +Clickstream Flutter SDK can help you easily collect and report events from your mobile app to AWS. This SDK is part of an AWS solution - [Clickstream Analytics on AWS](https://github.com/awslabs/clickstream-analytics-on-aws), which provisions data pipeline to ingest and process event data into AWS services such as S3, Redshift. -The SDK relies on the [Clickstream Android SDK](https://github.com/awslabs/clickstream-android) and [Clickstream Swift SDK](https://github.com/awslabs/clickstream-swift). Therefore, flutter SDK also supports automatically collect common user events and attributes (e.g., session start, first open) In addition, we've added easy-to-use APIs to simplify data collection in Flutter apps. +The SDK relies on the [Clickstream Android SDK](https://github.com/awslabs/clickstream-android) and [Clickstream Swift SDK](https://github.com/awslabs/clickstream-swift). Therefore, flutter SDK also supports automatically collect common user events and attributes (e.g., session start, first open). In addition, we've added easy-to-use APIs to simplify data collection in Flutter apps. ## Integrate SDK @@ -135,8 +135,8 @@ You can update the default configuration after initializing the SDK, below are t ```dart final analytics = ClickstreamAnalytics(); analytics.updateConfigure( - appId: "your appId", - endpoint: "https://example.com/collect", + appId: "your appId", + endpoint: "https://example.com/collect", isLogEvents: true, isCompressEvents: false, isTrackScreenViewEvents: false @@ -157,10 +157,24 @@ analytics.flushEvents(); ### Build +Install flutter packages + ```bash flutter pub get ``` +Build for Android + +```bash +cd example && flutter build apk +``` + +Build for iOS + +```dart +cd example && flutter build ios +``` + ### Format and lint ```bash diff --git a/example/README.md b/example/README.md index fc3f593..651e890 100644 --- a/example/README.md +++ b/example/README.md @@ -1,4 +1,4 @@ -# clickstream_flutter_example +# Clickstream Flutter SDK Example Demonstrates how to use the clickstream_flutter plugin. diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..f45292d --- /dev/null +++ b/release.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +version="$1" +echo ${version} +regex="[0-9]\+\.[0-9]\+\.[0-9]\+" + +sed -i "s/version: ${regex}/version: ${version}/g" pubspec.yaml