Skip to content

Commit 8363f51

Browse files
zhu-xiaoweizhu-xiaowei
andauthored
feat: add record screen view api (#8)
Co-authored-by: zhu-xiaowei <xiaoweii@amazom.com>
1 parent 217f286 commit 8363f51

File tree

10 files changed

+221
-156
lines changed

10 files changed

+221
-156
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ jobs:
1919
flutter pub get
2020
flutter test --coverage --reporter github
2121
- name: Upload Test Report
22-
uses: codecov/codecov-action@v3
22+
uses: codecov/codecov-action@v4
2323
with:
24+
token: ${{ secrets.CODECOV_TOKEN }}
2425
name: report
2526
files: coverage/lcov.info

README.md

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
[![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)
33

44

5-
65
## Introduction
76

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

9190
#### Add global attribute
9291

93-
```dart
94-
analytics.addGlobalAttributes({
95-
"_traffic_source_medium": "Search engine",
96-
"_traffic_source_name": "Summer promotion",
97-
"level": 10
98-
});
99-
100-
// delete global attribute
92+
1. Add global attributes when initializing the SDK
93+
94+
```dart
95+
analytics.init({
96+
appId: "your appId",
97+
endpoint: "https://example.com/collect",
98+
globalAttributes: {
99+
"_traffic_source_medium": "Search engine",
100+
"_traffic_source_name": "Summer promotion",
101+
}
102+
});
103+
```
104+
105+
2. Add global attributes after initializing the SDK
106+
```dart
107+
analytics.addGlobalAttributes({
108+
"_traffic_source_medium": "Search engine",
109+
"_traffic_source_name": "Summer promotion",
110+
"level": 10
111+
});
112+
```
113+
114+
#### Delete global attribute
115+
```
101116
analytics.deleteGlobalAttributes(["level"]);
102117
```
103118

@@ -123,13 +138,30 @@ var itemBook = ClickstreamItem(
123138
analytics.record(
124139
name: "view_item",
125140
attributes: {
126-
"currency": 'USD',
127-
"event_category": 'recommended'
141+
"currency": "USD",
142+
"event_category": "recommended"
128143
},
129144
items: [itemBook]
130145
);
131146
```
132147

148+
#### Record Screen View events manually
149+
150+
By default, SDK will automatically track the preset `_screen_view` event when Android Activity triggers `onResume` or iOS ViewController triggers `viewDidAppear`.
151+
152+
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.
153+
154+
* `screenName` Required. Your screen's name.
155+
* `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.
156+
157+
```dart
158+
analytics.recordScreenView(
159+
screenName: 'Main',
160+
screenUniqueId: '123adf',
161+
attributes: { ... }
162+
);
163+
```
164+
133165
#### Other configurations
134166

135167
In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage:
@@ -146,7 +178,10 @@ analytics.init(
146178
isTrackUserEngagementEvents: true,
147179
isTrackAppExceptionEvents: false,
148180
authCookie: "your auth cookie",
149-
sessionTimeoutDuration: 1800000
181+
sessionTimeoutDuration: 1800000,
182+
globalAttributes: {
183+
"_traffic_source_medium": "Search engine",
184+
},
150185
);
151186
```
152187

@@ -162,6 +197,7 @@ Here is an explanation of each option:
162197
- **isTrackAppExceptionEvents**: whether auto track exception event in app, default is `false`
163198
- **authCookie**: your auth cookie for AWS application load balancer auth cookie.
164199
- **sessionTimeoutDuration**: the duration for session timeout millisecond, default is 1800000
200+
- **globalAttributes**: the global attributes when initializing the SDK.
165201

166202
#### Configuration update
167203

@@ -177,7 +213,6 @@ analytics.updateConfigure(
177213
isTrackScreenViewEvents: false
178214
isTrackUserEngagementEvents: false,
179215
isTrackAppExceptionEvents: false,
180-
sessionTimeoutDuration: 100000,
181216
authCookie: "test cookie");
182217
```
183218

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ android {
5252
dependencies {
5353
testImplementation 'org.jetbrains.kotlin:kotlin-test'
5454
testImplementation 'org.mockito:mockito-core:5.0.0'
55-
implementation 'software.aws.solution:clickstream:0.10.0'
55+
implementation 'software.aws.solution:clickstream:0.12.0'
5656
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10"))
5757
}
5858

android/src/main/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPlugin.kt

Lines changed: 62 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,17 @@ package software.aws.solution.clickstream_analytics
1717
import android.app.Activity
1818
import com.amazonaws.logging.Log
1919
import com.amazonaws.logging.LogFactory
20-
import com.amplifyframework.AmplifyException
2120
import com.amplifyframework.core.Amplify
22-
import com.amplifyframework.core.AmplifyConfiguration
2321
import io.flutter.embedding.engine.plugins.FlutterPlugin
2422
import io.flutter.embedding.engine.plugins.activity.ActivityAware
2523
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
2624
import io.flutter.plugin.common.MethodCall
2725
import io.flutter.plugin.common.MethodChannel
2826
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
2927
import io.flutter.plugin.common.MethodChannel.Result
30-
import org.json.JSONObject
31-
import software.aws.solution.clickstream.AWSClickstreamPlugin
3228
import software.aws.solution.clickstream.ClickstreamAnalytics
3329
import software.aws.solution.clickstream.ClickstreamAttribute
30+
import software.aws.solution.clickstream.ClickstreamConfiguration
3431
import software.aws.solution.clickstream.ClickstreamEvent
3532
import software.aws.solution.clickstream.ClickstreamItem
3633
import software.aws.solution.clickstream.ClickstreamUserAttribute
@@ -64,53 +61,20 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
6461
override fun onMethodCall(call: MethodCall, result: Result) {
6562
val arguments = call.arguments() as HashMap<String, Any>?
6663
when (call.method) {
67-
"init" -> {
68-
result.success(initSDK(arguments!!))
69-
}
70-
71-
"record" -> {
72-
recordEvent(arguments)
73-
}
74-
75-
"setUserId" -> {
76-
setUserId(arguments)
77-
}
78-
79-
"setUserAttributes" -> {
80-
setUserAttributes(arguments)
81-
}
82-
83-
"setGlobalAttributes" -> {
84-
setGlobalAttributes(arguments)
85-
}
86-
87-
"deleteGlobalAttributes" -> {
88-
deleteGlobalAttributes(arguments)
89-
}
90-
91-
"updateConfigure" -> {
92-
updateConfigure(arguments)
93-
}
94-
95-
"flushEvents" -> {
96-
ClickstreamAnalytics.flushEvents()
97-
}
98-
99-
"disable" -> {
100-
ClickstreamAnalytics.disable()
101-
}
102-
103-
"enable" -> {
104-
ClickstreamAnalytics.enable()
105-
}
106-
107-
else -> {
108-
result.notImplemented()
109-
}
64+
"init" -> result.success(initSDK(arguments!!))
65+
"record" -> recordEvent(arguments)
66+
"setUserId" -> setUserId(arguments)
67+
"setUserAttributes" -> setUserAttributes(arguments)
68+
"addGlobalAttributes" -> addGlobalAttributes(arguments)
69+
"deleteGlobalAttributes" -> deleteGlobalAttributes(arguments)
70+
"updateConfigure" -> updateConfigure(arguments)
71+
"flushEvents" -> ClickstreamAnalytics.flushEvents()
72+
"disable" -> ClickstreamAnalytics.disable()
73+
"enable" -> ClickstreamAnalytics.enable()
74+
else -> result.notImplemented()
11075
}
11176
}
11277

113-
11478
private fun initSDK(arguments: HashMap<String, Any>): Boolean {
11579
if (getIsInitialized()) return false
11680
if (mActivity != null) {
@@ -119,28 +83,13 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
11983
log.error("Clickstream SDK initialization failed, please initialize in the main thread")
12084
return false
12185
}
122-
val amplifyObject = JSONObject()
123-
val analyticsObject = JSONObject()
124-
val pluginsObject = JSONObject()
125-
val awsClickstreamPluginObject = JSONObject()
126-
awsClickstreamPluginObject.put("appId", arguments["appId"])
127-
awsClickstreamPluginObject.put("endpoint", arguments["endpoint"])
128-
pluginsObject.put("awsClickstreamPlugin", awsClickstreamPluginObject)
129-
analyticsObject.put("plugins", pluginsObject)
130-
amplifyObject.put("analytics", analyticsObject)
131-
val configure = AmplifyConfiguration.fromJson(amplifyObject)
132-
try {
133-
Amplify.addPlugin<AWSClickstreamPlugin>(AWSClickstreamPlugin(context))
134-
Amplify.configure(configure, context)
135-
} catch (exception: AmplifyException) {
136-
log.error("Clickstream SDK initialization failed with error: " + exception.message)
137-
return false
138-
}
13986
val sessionTimeoutDuration = arguments["sessionTimeoutDuration"]
14087
.let { (it as? Int)?.toLong() ?: (it as Long) }
14188
val sendEventsInterval = arguments["sendEventsInterval"]
14289
.let { (it as? Int)?.toLong() ?: (it as Long) }
143-
ClickstreamAnalytics.getClickStreamConfiguration()
90+
val configuration = ClickstreamConfiguration()
91+
.withAppId(arguments["appId"] as String)
92+
.withEndpoint(arguments["endpoint"] as String)
14493
.withLogEvents(arguments["isLogEvents"] as Boolean)
14594
.withTrackScreenViewEvents(arguments["isTrackScreenViewEvents"] as Boolean)
14695
.withTrackUserEngagementEvents(arguments["isTrackUserEngagementEvents"] as Boolean)
@@ -149,7 +98,28 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
14998
.withSessionTimeoutDuration(sessionTimeoutDuration)
15099
.withCompressEvents(arguments["isCompressEvents"] as Boolean)
151100
.withAuthCookie(arguments["authCookie"] as String)
152-
return true
101+
102+
(arguments["globalAttributes"] as? HashMap<*, *>)?.takeIf { it.isNotEmpty() }
103+
?.let { attributes ->
104+
val globalAttributes = ClickstreamAttribute.builder()
105+
for ((key, value) in attributes) {
106+
when (value) {
107+
is String -> globalAttributes.add(key.toString(), value)
108+
is Double -> globalAttributes.add(key.toString(), value)
109+
is Boolean -> globalAttributes.add(key.toString(), value)
110+
is Int -> globalAttributes.add(key.toString(), value)
111+
is Long -> globalAttributes.add(key.toString(), value)
112+
}
113+
}
114+
configuration.withInitialGlobalAttributes(globalAttributes.build())
115+
}
116+
return try {
117+
ClickstreamAnalytics.init(context, configuration)
118+
true
119+
} catch (exception: Exception) {
120+
log.error("Clickstream SDK initialization failed with error: " + exception.message)
121+
false
122+
}
153123
} else {
154124
return false
155125
}
@@ -163,33 +133,25 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
163133
val items = it["items"] as ArrayList<*>
164134
val eventBuilder = ClickstreamEvent.builder().name(eventName)
165135
for ((key, value) in attributes) {
166-
if (value is String) {
167-
eventBuilder.add(key.toString(), value)
168-
} else if (value is Double) {
169-
eventBuilder.add(key.toString(), value)
170-
} else if (value is Boolean) {
171-
eventBuilder.add(key.toString(), value)
172-
} else if (value is Int) {
173-
eventBuilder.add(key.toString(), value)
174-
} else if (value is Long) {
175-
eventBuilder.add(key.toString(), value)
136+
when (value) {
137+
is String -> eventBuilder.add(key.toString(), value)
138+
is Double -> eventBuilder.add(key.toString(), value)
139+
is Boolean -> eventBuilder.add(key.toString(), value)
140+
is Int -> eventBuilder.add(key.toString(), value)
141+
is Long -> eventBuilder.add(key.toString(), value)
176142
}
177143
}
178144
if (items.size > 0) {
179145
val clickstreamItems = arrayOfNulls<ClickstreamItem>(items.size)
180146
for (index in 0 until items.size) {
181147
val builder = ClickstreamItem.builder()
182148
for ((key, value) in (items[index] as HashMap<*, *>)) {
183-
if (value is String) {
184-
builder.add(key.toString(), value)
185-
} else if (value is Double) {
186-
builder.add(key.toString(), value)
187-
} else if (value is Boolean) {
188-
builder.add(key.toString(), value)
189-
} else if (value is Int) {
190-
builder.add(key.toString(), value)
191-
} else if (value is Long) {
192-
builder.add(key.toString(), value)
149+
when (value) {
150+
is String -> builder.add(key.toString(), value)
151+
is Double -> builder.add(key.toString(), value)
152+
is Boolean -> builder.add(key.toString(), value)
153+
is Int -> builder.add(key.toString(), value)
154+
is Long -> builder.add(key.toString(), value)
193155
}
194156
}
195157
clickstreamItems[index] = builder.build()
@@ -217,36 +179,28 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
217179
arguments?.let {
218180
val builder = ClickstreamUserAttribute.Builder()
219181
for ((key, value) in arguments) {
220-
if (value is String) {
221-
builder.add(key, value)
222-
} else if (value is Double) {
223-
builder.add(key, value)
224-
} else if (value is Boolean) {
225-
builder.add(key, value)
226-
} else if (value is Int) {
227-
builder.add(key, value)
228-
} else if (value is Long) {
229-
builder.add(key, value)
182+
when (value) {
183+
is String -> builder.add(key, value)
184+
is Double -> builder.add(key, value)
185+
is Boolean -> builder.add(key, value)
186+
is Int -> builder.add(key, value)
187+
is Long -> builder.add(key, value)
230188
}
231189
}
232190
ClickstreamAnalytics.addUserAttributes(builder.build())
233191
}
234192
}
235193

236-
private fun setGlobalAttributes(arguments: java.util.HashMap<String, Any>?) {
194+
private fun addGlobalAttributes(arguments: java.util.HashMap<String, Any>?) {
237195
arguments?.let {
238196
val builder = ClickstreamAttribute.Builder()
239197
for ((key, value) in arguments) {
240-
if (value is String) {
241-
builder.add(key, value)
242-
} else if (value is Double) {
243-
builder.add(key, value)
244-
} else if (value is Boolean) {
245-
builder.add(key, value)
246-
} else if (value is Int) {
247-
builder.add(key, value)
248-
} else if (value is Long) {
249-
builder.add(key, value)
198+
when (value) {
199+
is String -> builder.add(key, value)
200+
is Double -> builder.add(key, value)
201+
is Boolean -> builder.add(key, value)
202+
is Int -> builder.add(key, value)
203+
is Long -> builder.add(key, value)
250204
}
251205
}
252206
ClickstreamAnalytics.addGlobalAttributes(builder.build())

0 commit comments

Comments
 (0)