From 53a21871189a01652879544f139d9fdc227a7fbe Mon Sep 17 00:00:00 2001 From: xiaoweii Date: Thu, 19 Oct 2023 09:05:25 +0800 Subject: [PATCH] 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'); });