Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature/#921] 알림 앰플리튜드 적용 #984

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading