Skip to content

Commit

Permalink
feat: add record screen view api (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: zhu-xiaowei <[email protected]>
  • Loading branch information
zhu-xiaowei and zhu-xiaowei authored Apr 9, 2024
1 parent 217f286 commit 8363f51
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 156 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
flutter pub get
flutter test --coverage --reporter github
- name: Upload Test Report
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
name: report
files: coverage/lcov.info
61 changes: 48 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
[![clickstream-flutter-test](https://github.com/awslabs/clickstream-flutter/actions/workflows/test.yml/badge.svg)](https://github.com/awslabs/clickstream-flutter/actions/workflows/test.yml) [![clickstream-flutter-release](https://github.com/awslabs/clickstream-flutter/actions/workflows/release.yml/badge.svg)](https://github.com/awslabs/clickstream-flutter/actions/workflows/release.yml) [![clickstream-flutter-build-android](https://github.com/awslabs/clickstream-flutter/actions/workflows/build-android.yml/badge.svg)](https://github.com/awslabs/clickstream-flutter/actions/workflows/build-android.yml) [![clickstream-flutter-build-ios](https://github.com/awslabs/clickstream-flutter/actions/workflows/build-ios.yml/badge.svg)](https://github.com/awslabs/clickstream-flutter/actions/workflows/build-ios.yml) [![pub package](https://img.shields.io/pub/v/clickstream_analytics.svg)](https://pub.dev/packages/clickstream_analytics) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)



## Introduction

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.
Expand Down Expand Up @@ -90,14 +89,30 @@ Current login user's attributes will be cached in disk, so the next time app lau

#### Add global attribute

```dart
analytics.addGlobalAttributes({
"_traffic_source_medium": "Search engine",
"_traffic_source_name": "Summer promotion",
"level": 10
});
// delete global attribute
1. Add global attributes when initializing the SDK

```dart
analytics.init({
appId: "your appId",
endpoint: "https://example.com/collect",
globalAttributes: {
"_traffic_source_medium": "Search engine",
"_traffic_source_name": "Summer promotion",
}
});
```

2. Add global attributes after initializing the SDK
```dart
analytics.addGlobalAttributes({
"_traffic_source_medium": "Search engine",
"_traffic_source_name": "Summer promotion",
"level": 10
});
```

#### Delete global attribute
```
analytics.deleteGlobalAttributes(["level"]);
```

Expand All @@ -123,13 +138,30 @@ var itemBook = ClickstreamItem(
analytics.record(
name: "view_item",
attributes: {
"currency": 'USD',
"event_category": 'recommended'
"currency": "USD",
"event_category": "recommended"
},
items: [itemBook]
);
```

#### Record Screen View events manually

By default, SDK will automatically track the preset `_screen_view` event when Android Activity triggers `onResume` or iOS ViewController triggers `viewDidAppear`.

You can also manually record screen view events whether automatic screen view tracking is enabled, add the following code to record a screen view event with two attributes.

* `screenName` Required. Your screen's name.
* `screenUniqueId` Optional. Set the id of your Widget. If you do not set, the SDK will set a default value based on the hashcode of the current Activity or ViewController.

```dart
analytics.recordScreenView(
screenName: 'Main',
screenUniqueId: '123adf',
attributes: { ... }
);
```

#### Other configurations

In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage:
Expand All @@ -146,7 +178,10 @@ analytics.init(
isTrackUserEngagementEvents: true,
isTrackAppExceptionEvents: false,
authCookie: "your auth cookie",
sessionTimeoutDuration: 1800000
sessionTimeoutDuration: 1800000,
globalAttributes: {
"_traffic_source_medium": "Search engine",
},
);
```

Expand All @@ -162,6 +197,7 @@ Here is an explanation of each option:
- **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
- **globalAttributes**: the global attributes when initializing the SDK.

#### Configuration update

Expand All @@ -177,7 +213,6 @@ analytics.updateConfigure(
isTrackScreenViewEvents: false
isTrackUserEngagementEvents: false,
isTrackAppExceptionEvents: false,
sessionTimeoutDuration: 100000,
authCookie: "test cookie");
```

Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ android {
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
implementation 'software.aws.solution:clickstream:0.10.0'
implementation 'software.aws.solution:clickstream:0.12.0'
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,17 @@ package software.aws.solution.clickstream_analytics
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
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.ClickstreamConfiguration
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem
import software.aws.solution.clickstream.ClickstreamUserAttribute
Expand Down Expand Up @@ -64,53 +61,20 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
override fun onMethodCall(call: MethodCall, result: Result) {
val arguments = call.arguments() as HashMap<String, Any>?
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)
}

"flushEvents" -> {
ClickstreamAnalytics.flushEvents()
}

"disable" -> {
ClickstreamAnalytics.disable()
}

"enable" -> {
ClickstreamAnalytics.enable()
}

else -> {
result.notImplemented()
}
"init" -> result.success(initSDK(arguments!!))
"record" -> recordEvent(arguments)
"setUserId" -> setUserId(arguments)
"setUserAttributes" -> setUserAttributes(arguments)
"addGlobalAttributes" -> addGlobalAttributes(arguments)
"deleteGlobalAttributes" -> deleteGlobalAttributes(arguments)
"updateConfigure" -> updateConfigure(arguments)
"flushEvents" -> ClickstreamAnalytics.flushEvents()
"disable" -> ClickstreamAnalytics.disable()
"enable" -> ClickstreamAnalytics.enable()
else -> result.notImplemented()
}
}


private fun initSDK(arguments: HashMap<String, Any>): Boolean {
if (getIsInitialized()) return false
if (mActivity != null) {
Expand All @@ -119,28 +83,13 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
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>(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()
val configuration = ClickstreamConfiguration()
.withAppId(arguments["appId"] as String)
.withEndpoint(arguments["endpoint"] as String)
.withLogEvents(arguments["isLogEvents"] as Boolean)
.withTrackScreenViewEvents(arguments["isTrackScreenViewEvents"] as Boolean)
.withTrackUserEngagementEvents(arguments["isTrackUserEngagementEvents"] as Boolean)
Expand All @@ -149,7 +98,28 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
.withSessionTimeoutDuration(sessionTimeoutDuration)
.withCompressEvents(arguments["isCompressEvents"] as Boolean)
.withAuthCookie(arguments["authCookie"] as String)
return true

(arguments["globalAttributes"] as? HashMap<*, *>)?.takeIf { it.isNotEmpty() }
?.let { attributes ->
val globalAttributes = ClickstreamAttribute.builder()
for ((key, value) in attributes) {
when (value) {
is String -> globalAttributes.add(key.toString(), value)
is Double -> globalAttributes.add(key.toString(), value)
is Boolean -> globalAttributes.add(key.toString(), value)
is Int -> globalAttributes.add(key.toString(), value)
is Long -> globalAttributes.add(key.toString(), value)
}
}
configuration.withInitialGlobalAttributes(globalAttributes.build())
}
return try {
ClickstreamAnalytics.init(context, configuration)
true
} catch (exception: Exception) {
log.error("Clickstream SDK initialization failed with error: " + exception.message)
false
}
} else {
return false
}
Expand All @@ -163,33 +133,25 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
val items = it["items"] as ArrayList<*>
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)
when (value) {
is String -> eventBuilder.add(key.toString(), value)
is Double -> eventBuilder.add(key.toString(), value)
is Boolean -> eventBuilder.add(key.toString(), value)
is Int -> eventBuilder.add(key.toString(), value)
is Long -> eventBuilder.add(key.toString(), value)
}
}
if (items.size > 0) {
val clickstreamItems = arrayOfNulls<ClickstreamItem>(items.size)
for (index in 0 until items.size) {
val builder = ClickstreamItem.builder()
for ((key, value) in (items[index] as HashMap<*, *>)) {
if (value is String) {
builder.add(key.toString(), value)
} else if (value is Double) {
builder.add(key.toString(), value)
} else if (value is Boolean) {
builder.add(key.toString(), value)
} else if (value is Int) {
builder.add(key.toString(), value)
} else if (value is Long) {
builder.add(key.toString(), value)
when (value) {
is String -> builder.add(key.toString(), value)
is Double -> builder.add(key.toString(), value)
is Boolean -> builder.add(key.toString(), value)
is Int -> builder.add(key.toString(), value)
is Long -> builder.add(key.toString(), value)
}
}
clickstreamItems[index] = builder.build()
Expand Down Expand Up @@ -217,36 +179,28 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
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)
when (value) {
is String -> builder.add(key, value)
is Double -> builder.add(key, value)
is Boolean -> builder.add(key, value)
is Int -> builder.add(key, value)
is Long -> builder.add(key, value)
}
}
ClickstreamAnalytics.addUserAttributes(builder.build())
}
}

private fun setGlobalAttributes(arguments: java.util.HashMap<String, Any>?) {
private fun addGlobalAttributes(arguments: java.util.HashMap<String, Any>?) {
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)
when (value) {
is String -> builder.add(key, value)
is Double -> builder.add(key, value)
is Boolean -> builder.add(key, value)
is Int -> builder.add(key, value)
is Long -> builder.add(key, value)
}
}
ClickstreamAnalytics.addGlobalAttributes(builder.build())
Expand Down
Loading

0 comments on commit 8363f51

Please sign in to comment.