Skip to content

Commit

Permalink
feat: implement Notification Amplitude
Browse files Browse the repository at this point in the history
  • Loading branch information
giovannijunseokim committed Dec 24, 2024
1 parent 7089da6 commit 96ae170
Show file tree
Hide file tree
Showing 7 changed files with 360 additions and 221 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,72 +35,98 @@ import com.skydoves.firebase.messaging.lifecycle.ktx.LifecycleAwareFirebaseMessa
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.sopt.official.R
import org.sopt.official.analytics.AmplitudeTracker
import org.sopt.official.analytics.EventType
import org.sopt.official.auth.model.UserStatus
import org.sopt.official.common.navigator.DeepLinkType
import org.sopt.official.domain.notification.usecase.RegisterPushTokenUseCase
import org.sopt.official.feature.notification.SchemeActivity
import org.sopt.official.feature.notification.SchemeActivity.Argument.NotificationInfo
import org.sopt.official.network.persistence.SoptDataStore
import javax.inject.Inject

@AndroidEntryPoint
class SoptFirebaseMessagingService : LifecycleAwareFirebaseMessagingService() {

@Inject
lateinit var dataStore: SoptDataStore
@Inject
lateinit var dataStore: SoptDataStore

@Inject
lateinit var registerPushTokenUseCase: RegisterPushTokenUseCase
@Inject
lateinit var registerPushTokenUseCase: RegisterPushTokenUseCase

override fun onNewToken(token: String) {
if (dataStore.userStatus == UserStatus.UNAUTHENTICATED.name) return
lifecycleScope.launch {
dataStore.pushToken = token
registerPushTokenUseCase.invoke(token)
@Inject
lateinit var tracker: AmplitudeTracker

override fun onNewToken(token: String) {
if (dataStore.userStatus == UserStatus.UNAUTHENTICATED.name) return
lifecycleScope.launch {
dataStore.pushToken = token
registerPushTokenUseCase.invoke(token)
}
}
}

override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
if (remoteMessage.data.isEmpty()) return
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
if (remoteMessage.data.isEmpty()) return

val receivedData = remoteMessage.data
val notificationId = receivedData["id"] ?: ""
val title = receivedData["title"] ?: ""
val body = receivedData["content"] ?: ""
val webLink = receivedData["webLink"] ?: ""
val deepLink = receivedData["deepLink"] ?: ""
val receivedData = remoteMessage.data
val notificationId = receivedData["id"] ?: ""
val title = receivedData["title"] ?: ""
val body = receivedData["content"] ?: ""
val category = receivedData["category"] ?: ""
val deepLink = receivedData["deepLink"] ?: ""
val webLink = receivedData["webLink"] ?: ""
val sendAt = receivedData["sendAt"] ?: ""
val relatedFeature = DeepLinkType.of(deepLink).name

val notifyId = System.currentTimeMillis().toInt()
val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).setContentTitle(title).setContentText(body)
.setStyle(NotificationCompat.BigTextStyle().bigText(body)).setSmallIcon(R.drawable.img_logo_small)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC).setChannelId(getString(R.string.toolbar_notification)).setAutoCancel(true)
tracker.track(
type = EventType.RECEIVED,
name = "push",
properties = mapOf(
"notification_id" to notificationId,
"send_timestamp" to sendAt,
"title" to title,
"contents" to body,
"relatedfeature" to relatedFeature,
"admin_category" to category
)
)

notificationBuilder.setNotificationContentIntent(
notificationId, webLink.ifBlank { deepLink.ifBlank { "" } }, notifyId
)
val notifyId = System.currentTimeMillis().toInt()
val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).setContentTitle(title).setContentText(body)
.setStyle(NotificationCompat.BigTextStyle().bigText(body)).setSmallIcon(R.drawable.img_logo_small)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC).setChannelId(getString(R.string.toolbar_notification)).setAutoCancel(true)

val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(notifyId, notificationBuilder.build())
}
notificationBuilder.setNotificationContentIntent(
notificationId,
webLink.ifBlank { deepLink.ifBlank { "" } },
notifyId,
NotificationInfo(id = notificationId, sendAt = sendAt, title = title, content = body, relatedFeature = relatedFeature)
)

private fun NotificationCompat.Builder.setNotificationContentIntent(
notificationId: String, link: String, notifyId: Int
): NotificationCompat.Builder {
val intent = SchemeActivity.getIntent(
this@SoptFirebaseMessagingService, SchemeActivity.Argument(notificationId, link)
)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(notifyId, notificationBuilder.build())
}

return this.setContentIntent(
PendingIntent.getActivity(
this@SoptFirebaseMessagingService, notifyId, intent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
}
)
)
}
private fun NotificationCompat.Builder.setNotificationContentIntent(
notificationId: String, link: String, notifyId: Int, notificationInfo: NotificationInfo
): NotificationCompat.Builder {
val intent = SchemeActivity.getIntent(
this@SoptFirebaseMessagingService, SchemeActivity.Argument(notificationId, link, notificationInfo)
)

companion object {
const val NOTIFICATION_CHANNEL_ID = "SOPT"
}
return this.setContentIntent(
PendingIntent.getActivity(
this@SoptFirebaseMessagingService, notifyId, intent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
}
)
)
}

companion object {
const val NOTIFICATION_CHANNEL_ID = "SOPT"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
tracker.track(type = EventType.VIEW, name = "apphome", properties = mapOf("view_type" to args?.userStatus?.value))
tracker.track(
type = EventType.VIEW,
name = "apphome",
properties = mapOf("view_type" to args?.userStatus?.value, "view_type" to args?.userStatus?.value)
)

requestNotificationPermission()
initToolbar()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,21 @@ import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.EntryPointAccessors
import org.sopt.official.analytics.AmplitudeTracker
import org.sopt.official.analytics.EventType
import org.sopt.official.auth.model.UserStatus
import org.sopt.official.common.navigator.DeepLinkType
import org.sopt.official.common.util.extractQueryParameter
import org.sopt.official.common.util.isExpiredDate
import org.sopt.official.common.util.serializableExtra
import org.sopt.official.feature.notification.SchemeActivity.Argument.NotificationInfo
import org.sopt.official.feature.notification.detail.NotificationDetailActivity
import org.sopt.official.network.persistence.SoptDataStoreEntryPoint
import timber.log.Timber
import java.io.Serializable
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import javax.inject.Inject

class SchemeActivity : AppCompatActivity() {
private val dataStore by lazy {
Expand All @@ -49,11 +55,28 @@ class SchemeActivity : AppCompatActivity() {
}
private val args by serializableExtra(Argument("", ""))

@Inject
lateinit var tracker: AmplitudeTracker

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
trackClickPush()
handleDeepLink()
}

private fun trackClickPush() {
args?.notificationInfo?.let { notificationInfo: NotificationInfo ->
tracker.track(
type = EventType.CLICK, name = "push", properties = mapOf(
"notification_id" to notificationInfo.id,
"send_timestamp" to notificationInfo.sendAt,
"leadtime" to ChronoUnit.DAYS.between(LocalDate.parse(notificationInfo.sendAt), LocalDate.now()),
"deeplink_url" to args?.link
)
)
}
}

private fun handleDeepLink() {
val link = args?.link
val linkIntent = if (link.isNullOrBlank()) {
Expand All @@ -63,7 +86,10 @@ class SchemeActivity : AppCompatActivity() {
)
} else {
checkLinkExpiration(link)
}
}.putExtra(
NotificationDetailActivity.EXTRA_FROM,
NotificationDetailActivity.Companion.OpenMethod.PUSH.korName
)

when (!isTaskRoot) {
true -> startActivity(linkIntent)
Expand Down Expand Up @@ -114,10 +140,23 @@ class SchemeActivity : AppCompatActivity() {
return intent.action == Intent.ACTION_MAIN && (intent.categories?.contains(Intent.CATEGORY_LAUNCHER) == true)
}

/**
*@param notificationInfo 푸시 알림을 클릭해서 들어온 경우, 푸시 알림에 관한 정보를 제공합니다.
* 푸시 알림을 통해 들어온 것이 아닐 경우에는 null 입니다.
* */
data class Argument(
val notificationId: String,
val link: String
) : Serializable
val link: String,
val notificationInfo: NotificationInfo? = null
) : Serializable {
data class NotificationInfo(
val id: String,
val sendAt: String,
val title: String,
val content: String,
val relatedFeature: String,
) : Serializable
}

companion object {
@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ package org.sopt.official.analytics

enum class EventType(val prefix: String) {
VIEW("view"),
CLICK("click")
CLICK("click"),
RECEIVED("received")
}
Loading

0 comments on commit 96ae170

Please sign in to comment.