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

[AN/USER] feat: fcm 푸시 알림 구현 (#522) #537

Merged
merged 17 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 13 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
2 changes: 1 addition & 1 deletion android/festago/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
android:exported="false" />

<service
android:name=".presentation.service.TicketEntryService"
android:name=".presentation.fcm.TicketEntryService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.festago.festago

import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import com.festago.festago.presentation.fcm.FcmMessageType
import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.HiltAndroidApp

Expand All @@ -10,9 +14,21 @@ class FestagoApplication : Application() {
override fun onCreate() {
super.onCreate()
initKakaoSdk()
initNotificationChannel()
}

private fun initKakaoSdk() {
KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
}

private fun initNotificationChannel() {
val channel = NotificationChannel(
FcmMessageType.ENTRY_ALERT.channelId,
getString(R.string.entry_alert_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.festago.festago.presentation.fcm

enum class FcmMessageType(val id: Int, val channelId: String) {
ENTRY_ALERT(id = 0, channelId = "ENTRY_ALERT"),
ENTRY_PROCESS(id = 1, channelId = "ENTRY_PROCESS"),
;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.festago.festago.presentation.fcm

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.festago.festago.R
import com.festago.festago.presentation.fcm.FcmMessageType.ENTRY_ALERT
import com.festago.festago.presentation.ui.home.HomeActivity

class NotificationManager(private val context: Context) {

private val intent = HomeActivity.getIntent(context).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

private val pendingIntent = PendingIntent.getActivity(
context,
HOME_REQUEST_CODE,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
Comment on lines +15 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entryAlertNotificationBuilder 를 초기화하기 위해서만 필요한 것 같아요!
프로퍼티로 있는 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아까 논의한대로 프로퍼티로 가지고 있을 필요는 없지만 싱글톤으로 관리하기 위해서 이대로 둘게요!


private val entryAlertNotificationBuilder =
NotificationCompat.Builder(context, ENTRY_ALERT.channelId)
.setSmallIcon(R.mipmap.ic_festago_logo_round)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setContentIntent(pendingIntent)

fun sendEntryAlertNotification(title: String, body: String) {
entryAlertNotificationBuilder
.setContentTitle(title)
.setContentText(body)

if (ActivityCompat.checkSelfPermission(
context,
android.Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분도 PermissionUtil안에서 Activity.Compat.checkPermission으로 빼내는 것이 어떨까요??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영완료!

) {
NotificationManagerCompat.from(context).notify(0, entryAlertNotificationBuilder.build())
}
}

companion object {
private const val HOME_REQUEST_CODE = 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.festago.festago.presentation.fcm

import com.festago.festago.presentation.fcm.FcmMessageType.ENTRY_ALERT
import com.festago.festago.presentation.fcm.FcmMessageType.ENTRY_PROCESS
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.runBlocking

class TicketEntryService : FirebaseMessagingService() {

private val notificationManager by lazy { NotificationManager(this) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Firebase Service 도 힐트로 주입할 수 있나요?


override fun onMessageReceived(remoteMessage: RemoteMessage) {
when (remoteMessage.notification?.channelId) {
ENTRY_ALERT.channelId -> handleEntryAlert(remoteMessage)

ENTRY_PROCESS.channelId -> {
runBlocking {
ticketStateChangeEvent.emit(Unit)
}
}

else -> Unit
}
}

private fun handleEntryAlert(remoteMessage: RemoteMessage) {
notificationManager.sendEntryAlertNotification(
remoteMessage.notification?.title ?: "",
remoteMessage.notification?.body ?: ""
)
}

override fun onNewToken(token: String) {
// TODO: 토큰이 변경되었을 때 처리
}

companion object {
val ticketStateChangeEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.festago.festago.presentation.ui.home
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
Expand All @@ -13,6 +15,7 @@ import com.festago.festago.presentation.ui.home.mypage.MyPageFragment
import com.festago.festago.presentation.ui.home.ticketlist.TicketListFragment
import com.festago.festago.presentation.ui.signin.SignInActivity
import com.festago.festago.presentation.util.repeatOnStarted
import com.festago.festago.presentation.util.requestNotificationPermission
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -23,11 +26,24 @@ class HomeActivity : AppCompatActivity() {

private val vm: HomeViewModel by viewModels()

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
if (!isGranted) {
Toast.makeText(
this,
getString(R.string.home_notification_permission_denied),
Toast.LENGTH_SHORT
).show()
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 함수로 분리할 수 있지 않을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수로 분리해서 내부 프로퍼티 제거했습니다!

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initBinding()
initView()
initObserve()
requestNotificationPermission(requestPermissionLauncher)
}

private fun initBinding() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import com.festago.festago.databinding.ActivityTicketEntryBinding
import com.festago.festago.presentation.service.TicketEntryService
import com.festago.festago.presentation.fcm.TicketEntryService
import com.festago.festago.presentation.util.repeatOnStarted
import com.google.zxing.BarcodeFormat
import com.journeyapps.barcodescanner.BarcodeEncoder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.festago.festago.presentation.util

import android.Manifest.permission.POST_NOTIFICATIONS
import android.app.Activity
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.ContextCompat

fun Activity.requestNotificationPermission(resultLauncher: ActivityResultLauncher<String>) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
resultLauncher.launch(POST_NOTIFICATIONS)
}
}
}
4 changes: 4 additions & 0 deletions android/festago/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<string name="home_bottom_navigation_festival">축제 목록</string>
<string name="home_bottom_navigation_ticket">티켓 목록</string>
<string name="home_bottom_navigation_user">마이페이지</string>
<string name="home_notification_permission_denied">알림 권한을 거부했습니다 :(</string>

<!-- Strings related to ticket reserve-->
<string name="ticket_reserve_tv_date_range">%1s ~ %1s</string>
Expand Down Expand Up @@ -129,4 +130,7 @@
<string name="select_school_tv_error_message">학교 목록 불러오기에 실패했습니다.</string>
<string name="select_school_tv_select_school">학교 선택</string>

<!-- Strings related to fcm -->
<string name="entry_alert_channel_name">공연 입장 알림</string>

</resources>