Skip to content

Commit e63df73

Browse files
authored
Add push notification tracking example (#6)
* initial push notification tracking stuff * update README.md * add google-services.json * correct logging tags
1 parent 149dd1f commit e63df73

File tree

8 files changed

+236
-3
lines changed

8 files changed

+236
-3
lines changed

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ buildscript {
1010
classpath "com.android.tools.build:gradle:4.1.2"
1111
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1212
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
13-
classpath("de.mannodermaus.gradle.plugins:android-junit5:1.7.1.1") // needed for android unit tests
13+
14+
// needed for android unit tests
15+
classpath("de.mannodermaus.gradle.plugins:android-junit5:1.7.1.1")
1416
// NOTE: Do not place your application dependencies here; they belong
1517
// in the individual module build.gradle files
18+
classpath 'com.google.gms:google-services:4.3.5'
1619
}
1720
}
1821

samples/kotlin-android-app/README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,31 @@ The sample app is configured to open links with the schema and hostname `https:/
2121
Here is how you can do it via adb
2222
```bash
2323
adb shell am start -W -a android.intent.action.VIEW -d "https://segment-sample.com?utm_source=cli\&utm_click=2" com.segment.analytics.next
24-
```
24+
```
25+
26+
## FCM
27+
This project is setup to track push notification received and opened events. This code is strictly optional and must be customized as per your needs. The code here is only for demonstration purposes
28+
### Setup
29+
- Add your FCM project's `google-services.json` to this folder
30+
- Modify `MyFirebaseService.kt` to customize the notification displayed to the user.
31+
- Here is how to send a push notification using cURL [this uses the legacy api](https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-using-the-legacy-app-server-protocols)
32+
```bash
33+
curl --request POST \
34+
--url https://fcm.googleapis.com/fcm/send \
35+
--header 'Authorization: key=<SERVER_KEY>' \
36+
--header 'Content-Type: application/json' \
37+
--data '{
38+
"data": {
39+
"title": "Hello World"
40+
"content": "You have mail",
41+
},
42+
"to": "<FCM_TOKEN_FOR_DEVICE>"
43+
}'
44+
```
45+
46+
### How it works
47+
- We have 2 core changes
48+
- MyFirebaseService.kt
49+
The core component to handle FCM push messages. This is responsible for handling the incoming message and assigning the intents for the notification. It is also responsible for firing the `Push Notification Received` event.
50+
- PushNotificationTracking.kt
51+
The analytics plugin responsible for firing the "Push Notification Tapped" event. This is a lifecycle plugin that will be invoked for any Activity onCreate.

samples/kotlin-android-app/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id 'com.android.application'
33
id 'kotlin-android'
44
id 'org.jetbrains.kotlin.plugin.serialization'
5+
id 'com.google.gms.google-services'
56
}
67

78
android {
@@ -55,6 +56,10 @@ dependencies {
5556

5657
implementation 'com.google.android.gms:play-services-ads:20.0.0'
5758

59+
implementation platform('com.google.firebase:firebase-bom:27.1.0')
60+
implementation 'com.google.firebase:firebase-analytics-ktx'
61+
implementation 'com.google.firebase:firebase-messaging:19.0.0'
62+
5863
testImplementation 'junit:junit:4.+'
5964
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
6065
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"project_info": {
3+
"project_number": "563414602026",
4+
"project_id": "segment-sample-6f133",
5+
"storage_bucket": "segment-sample-6f133.appspot.com"
6+
},
7+
"client": [
8+
{
9+
"client_info": {
10+
"mobilesdk_app_id": "1:563414602026:android:c7050fbaa3bcd8e6dd0e53",
11+
"android_client_info": {
12+
"package_name": "com.segment.analytics.next"
13+
}
14+
},
15+
"oauth_client": [
16+
{
17+
"client_id": "563414602026-9ao5va0s6mjhcpa1d6im903jlv1l20ue.apps.googleusercontent.com",
18+
"client_type": 3
19+
}
20+
],
21+
"api_key": [
22+
{
23+
"current_key": "AIzaSyCnJY7sJnjjUi89bH6K7TE8W9MCozjkpo4"
24+
}
25+
],
26+
"services": {
27+
"appinvite_service": {
28+
"other_platform_oauth_client": [
29+
{
30+
"client_id": "563414602026-9ao5va0s6mjhcpa1d6im903jlv1l20ue.apps.googleusercontent.com",
31+
"client_type": 3
32+
}
33+
]
34+
}
35+
}
36+
}
37+
],
38+
"configuration_version": "1"
39+
}

samples/kotlin-android-app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
android:scheme="https" />
4343
</intent-filter>
4444
</activity>
45+
<service
46+
android:name=".MyFirebaseService"
47+
android:exported="false">
48+
<intent-filter>
49+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
50+
</intent-filter>
51+
</service>
4552
</application>
4653

4754
</manifest>

samples/kotlin-android-app/src/main/java/com/segment/analytics/next/MainApplication.kt

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
package com.segment.analytics.next
22

3+
import android.app.Activity
34
import android.app.Application
5+
import android.os.Bundle
6+
import android.util.Log
7+
import com.google.android.gms.tasks.OnCompleteListener
8+
import com.google.firebase.messaging.FirebaseMessaging
49
import com.segment.analytics.*
510
import com.segment.analytics.next.plugins.AndroidAdvertisingIdPlugin
611
import com.segment.analytics.next.plugins.AndroidRecordScreenPlugin
12+
import com.segment.analytics.next.plugins.PushNotificationTracking
713
import com.segment.analytics.next.plugins.WebhookPlugin
814
import com.segment.analytics.platform.Plugin
15+
import com.segment.analytics.platform.plugins.android.AndroidLifecycle
916
import com.segment.analytics.utilities.*
1017
import kotlinx.coroutines.CoroutineScope
1118
import kotlinx.coroutines.Dispatchers
1219
import kotlinx.coroutines.SupervisorJob
20+
import kotlinx.serialization.json.buildJsonObject
21+
import kotlinx.serialization.json.put
1322
import java.util.concurrent.Executors
1423

1524
class MainApplication : Application() {
@@ -46,9 +55,28 @@ class MainApplication : Application() {
4655
}
4756

4857
})
58+
analytics.add(PushNotificationTracking)
4959
// A random webhook url to view your events
50-
analytics.add(WebhookPlugin("https://webhook.site/387c1740-f919-4446-a26e-a9a01ed28c8a", Executors.newSingleThreadExecutor()))
60+
analytics.add(
61+
WebhookPlugin(
62+
"https://webhook.site/387c1740-f919-4446-a26e-a9a01ed28c8a",
63+
Executors.newSingleThreadExecutor()
64+
)
65+
)
5166

5267
analytics.add(AndroidAdvertisingIdPlugin(this))
68+
69+
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
70+
if (!task.isSuccessful) {
71+
Log.w("SegmentSample", "Fetching FCM registration token failed", task.exception)
72+
return@OnCompleteListener
73+
}
74+
75+
// Get new FCM registration token
76+
val token = task.result
77+
78+
// Log and toast
79+
Log.d("SegmentSample", token)
80+
})
5381
}
5482
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.segment.analytics.next
2+
3+
import android.app.NotificationChannel
4+
import android.app.NotificationManager
5+
import android.app.PendingIntent
6+
import android.content.Intent
7+
import android.media.AudioAttributes
8+
import android.os.Build
9+
import android.util.Log
10+
import androidx.core.app.NotificationCompat
11+
import com.google.android.gms.tasks.OnCompleteListener
12+
import com.google.firebase.iid.FirebaseInstanceId
13+
import com.google.firebase.messaging.FirebaseMessagingService
14+
import com.google.firebase.messaging.RemoteMessage
15+
import kotlinx.serialization.json.buildJsonObject
16+
import kotlinx.serialization.json.put
17+
18+
class MyFirebaseService : FirebaseMessagingService() {
19+
20+
val TAG = "MyFirebaseService"
21+
22+
override fun onNewToken(s: String) {
23+
Log.e(TAG, s)
24+
FirebaseInstanceId.getInstance().instanceId
25+
.addOnCompleteListener(OnCompleteListener { task ->
26+
if (!task.isSuccessful) {
27+
Log.w(TAG, "getInstanceId failed", task.exception)
28+
return@OnCompleteListener
29+
}
30+
31+
// Get new Instance ID token
32+
val token = task.result.token
33+
Log.e(TAG, token)
34+
})
35+
}
36+
37+
override fun onMessageReceived(remoteMessage: RemoteMessage) {
38+
Log.d("SegmentSample", "push notification received")
39+
super.onMessageReceived(remoteMessage)
40+
val data = remoteMessage.data
41+
val title = data["title"]
42+
val body = data["content"]
43+
44+
MainApplication.analytics.track("Push Notification Received", buildJsonObject {
45+
val campaign = buildJsonObject {
46+
put("medium", "Push")
47+
put("source", "FCM")
48+
put("name", title)
49+
put("content", body)
50+
}
51+
put("campaign", campaign)
52+
})
53+
54+
val intent = Intent(applicationContext, MainActivity::class.java).apply {
55+
putExtra("push_notification", true)
56+
}
57+
val pi = PendingIntent.getActivity(applicationContext, 101, intent, 0)
58+
val nm = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
59+
var channel: NotificationChannel? = null
60+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
61+
val att = AudioAttributes.Builder()
62+
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
63+
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
64+
.build()
65+
channel = NotificationChannel("222", "my_channel", NotificationManager.IMPORTANCE_HIGH)
66+
nm.createNotificationChannel(channel)
67+
}
68+
val builder = NotificationCompat.Builder(applicationContext, "222")
69+
.setSmallIcon(R.mipmap.ic_launcher)
70+
.setContentTitle(body)
71+
.setAutoCancel(true)
72+
.setContentText(title)
73+
.setContentIntent(pi)
74+
.setDeleteIntent(pi)
75+
.setPriority(NotificationCompat.PRIORITY_HIGH)
76+
77+
nm.notify(123456789, builder.build())
78+
}
79+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.segment.analytics.next.plugins
2+
3+
import android.app.Activity
4+
import android.os.Bundle
5+
import android.util.Log
6+
import com.segment.analytics.Analytics
7+
import com.segment.analytics.platform.Plugin
8+
import com.segment.analytics.platform.plugins.android.AndroidLifecycle
9+
import kotlinx.serialization.json.buildJsonObject
10+
import kotlinx.serialization.json.put
11+
12+
object PushNotificationTracking: Plugin, AndroidLifecycle {
13+
override val type: Plugin.Type = Plugin.Type.Utility
14+
override val name: String = "PushNotificationTracking"
15+
override lateinit var analytics: Analytics
16+
17+
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
18+
val bundle = if (savedInstanceState == null) {
19+
activity?.intent?.extras
20+
} else {
21+
Bundle().apply {
22+
putAll(savedInstanceState)
23+
putAll(activity?.intent?.extras)
24+
}
25+
}
26+
checkPushNotification(bundle)
27+
}
28+
29+
private fun checkPushNotification(bundle: Bundle?) {
30+
if (bundle != null) {
31+
if (bundle.containsKey("push_notification")) {
32+
analytics.track("Push Notification Tapped", buildJsonObject {
33+
put("action", "Open")
34+
val campaign = buildJsonObject {
35+
put("medium", "Push")
36+
put("source", "FCM")
37+
put("name", bundle.getString("title"))
38+
put("content", bundle.getString("content"))
39+
}
40+
put("campaign", campaign)
41+
})
42+
}
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)