본문 바로가기

Android

Firebase Messaging 관리

Firebase를 관리하기 위해서는 다음 절차를 따라서 진행하면 됩니다.

 

1. Firebase Console에서 프로젝트를 만든다. 

 

2. 안드로이드 프로젝트로 시작을 한다. (프로젝트를 만들고 나서 앱 추가를 누르면 웹, 안드로이드, IOS 등 설정하라고 하는데 이 때 안드로이드를 선택하면 됩니다.)

3. build.gradle(App)에 ApplicationId에 내용을 아래 패키지 이름에 넣어서 만듭니다.  닉네임(임의로 입력), SHA-1 등은 그냥 넘어가도 됩니다.

4. google-services.json을 다운로드 받아서 안드로이드 프로젝트 > project > app에 해당 json 파일을 넣습니다.

5. build.gradle에 plugins { }에 google-services 추가합니다. 

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.gms.google-services'
}

6. dependencies에 implementation으로 사용할 것 추가

implementation platform('com.google.firebase:firebase-bom:29.3.1')
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'

7. build.gradle(Project)에 다음과 같이 내용 추가

buildscript {
    repositories {
        google()
    }

    dependencies {
        classpath 'com.google.gms:google-services:4.3.10'
    }
}

plugins {
    id 'com.android.application' version '7.1.2' apply false
    id 'com.android.library' version '7.1.2' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}

allprojects {
    repositories {
        google()
    }
}

여기서 순서도 중요합니다.  plugins 위에 allprojects 태그가 올라가면 오류가 발생할 것이므로 잘 정리해야 합니다.

 

8. settings.gradle에서 프로젝트 셋팅할 때 repositoriesMode 내용을 PREFER_SETTINGS로 변경합니다. 

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        google()
        mavenCentral()

    }
}

안드로이드 스튜디오가 범블비로 바뀌면서 생기는 이슈인데 PREFER_SETTINGS로 바꾸게 되면 오류가 사라집니다.

 

9. Firebase 인스턴스를 관리하는 액티비티를 구성

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    // 파이어베이스 토큰 가지고 오기
    initFirebase()
    updateResult()
}

// singleTop -> onNewIntent호출
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    setIntent(intent) // 새로 들어온 데이터로 대체할 것
    updateResult(true)
}

private fun initFirebase() {


    FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            val token = task.result // token 가지고 오기
            firebaseToken.text = token
        }
    }
}

// app이 기존에 켜져있는데 noti 눌러서 갱신?
// app이 꺼져있는데 noti로 킨건지?
private fun updateResult(isNewIntent: Boolean = false) {
    // false -> oncreate : 실행
    resultTextView.text =
        (intent.getStringExtra("notificationType")
            ?: "앱 런처") + if (isNewIntent) "(으)로 갱신되었습니다." else "(으)로 실행되었습니다."
}

FirebaseMessaging의 인스턴스의 토큰을 호출해서 해당 토큰을 잘 가져왔다는 리스너를 구현합니다.

task가 잘 수행되었으면 토큰은 task의 결과물이라고 처리합니다. 

(firebaseToken, resultTextView를 별도로 만든 텍스트뷰이므로 신경쓰지 않으셔도 됩니다.)

다음 작업을 수행하면 Firebase Token을 가지고 올 수 있습니다.

해당 토큰을 가지고 Firebase Console에서 토큰값을 메시지에 담아서 보내면 됩니다. (해당 디바이스에 메시지를 전송)

 

10. 문서 > 참조 > REST > Cloud Messaging > Send로 접근합니다.  

 

11. 메시지 전송 준비를 합니다.

 

12. 다운받은 google-services.json에 project_id를 가지고와서 parent에 넣어주고 request body에는 아래와 같이 셋팅합니다.

> projects/project_id

> message 내용에 토큰, data를 추가해줌

 

13. FirebaseMessagingService를 상속받는 클래스를 만들어줍니다. 

class MyFirebaseMessagingService : FirebaseMessagingService() {
    // Firebase Messaging에서 토큰 가지고 오기
    override fun onNewToken(token: String) {
        super.onNewToken(token) // 토큰 갱신될 때 마다 서버에 관리할 것
        sendRegistrationToServer(token)
    }

    private fun sendRegistrationToServer(token: String) {

    }

14. onMessageReceived 함수를 오버라이드해서 구현합니다.

실제 받은 메시지를 구분해서 앱에 이벤트를 주는 것으로 Notification과 PendingIntent 등을 구현하면 됩니다.

 

전송한 메시지에서 data 내 type, title, message를 따로 구현하였으므로 다음과 같이 값을 확인합니다. 

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)
    createNotificationChannel()
    // 보내는 타입 (json) 형태 체크
    val type = remoteMessage.data["type"]
        ?.let { NotificationType.valueOf(it) } // Nullable 가능

    val title = remoteMessage.data["title"]
    val message = remoteMessage.data["message"]
    type ?: return // null -> return

 

15. 채널 생성 

private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // channel 만들어야함

        val channel = NotificationChannel(
            CHANNEL_ID,
            CHANNEL_NAME,
            NotificationManager.IMPORTANCE_DEFAULT
        )

        channel.description = CHANNEL_DESCRIPTION
        channel.enableVibration(true)
        (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
            .createNotificationChannel(channel)
    }
    // channel 만들지 않아도 됨
    else {

    }
}

CHANNEL_ID, CHANNEL_NAME은 companion object로 상수값으로 구현해둔 것

 

16. 알림 콘텐츠 설정 

NotificationCompat.Builder 객체를 사용해서 알림 콘텐츠와 채널을 설정

Android 8.0(API 26이상)이상에서는 호환성을 유지하기 위해 필요하지만 이전 버전에서는 무시됩니다.

 

val intent = Intent(this,MainActivity::class.java).apply {
    putExtra("notificationType", "${type.title} 타입")
    addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
val pendingIntent = PendingIntent.getActivity(this, type.id, intent, FLAG_UPDATE_CURRENT)
val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_notifications)
    .setContentTitle(title)
    .setContentText(message)
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true) // 클릭하면 noti가 자동으로 닫힘

17. 알림의 탭 작업 설정을 위해 PendingIntent 객체로 정의된 콘텐츠 인텐트를 지정하여 setContentIntent()에 전달합니다.

위 그림처럼 정리할 것 (cf. setAutoCancel -> 사용자가 알림을 탭하면 자동으로 알림을 삭제하는 옵션)

 

NotificationCompat.Builder에서 스타일 설정과 리소스를 관리할 수 있습니다.

notificationBuilder.setStyle(
    NotificationCompat.DecoratedCustomViewStyle()
).setCustomContentView(
    RemoteViews(
        packageName, R.layout.view_custom_notification
    ).apply {
        setTextViewText(R.id.title, title)
        setTextViewText(R.id.message, message)
    }
)

https://developer.android.com/reference/androidx/core/app/NotificationCompat.DecoratedCustomViewStyle

 

NotificationCompat.DecoratedCustomViewStyle  |  Android Developers

androidx.car.app.managers

developer.android.com

NotificationCompat.DecoratedCustomViewStyle()로 하면 커스텀뷰를 가지고와서 사용할 수 있는데, setCustomContentView로 해서 레이아웃을 붙입니다. 

그리고 그 레이아웃 내 리소스(위젯)에 접근하기 위해서 apply를 통해 각 set함수를 사용하여 데이터를 붙여넣고 결과처리를 진행합니다.

 

 

18. NotificationManagerCompat.notify()는 고유 IDdhk NotificationCompat.Builder.build()의 결과를 전달합니다.

 

NotificationManagerCompat.from(this)
    .notify(
        type.id,
        createNotification(type, title, message)
    )
private fun createNotification(
    type: NotificationType,
    title: String?, // nullable로 줄 것
    message: String? // nullable로 줄 것
): Notification {
    val intent = Intent(this, MainActivity::class.java).apply {
        putExtra("notificationType", "${type.title} 타입")
        addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)

    }
    // requestcode = type.id
    // FLAG_UPDATE_CURRENT -> PendingIntent에 정의됨
    val pendingIntent = PendingIntent.getActivity(this, type.id, intent, FLAG_UPDATE_CURRENT)
    val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_notifications)
        .setContentTitle(title)
        .setContentText(message)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .setAutoCancel(true) // 클릭하면 noti가 자동으로 닫힘


    when (type) {
        NotificationType.NORMAL -> Unit // 아무것도 안함
        NotificationType.EXPANDABLE -> {
            notificationBuilder.setStyle(
                NotificationCompat.BigTextStyle()
                    .bigText(
                        "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃" +
                                "😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐" +
                                "🤓 😎 🥸 🤩 🥳 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 " +
                                "😫 😩 🥺 😢 😭 😤 😠 😡 🤬 🤯 😳 🥵 🥶 😱 😨 😰" +
                                "😥 😓 🤗 🤔 🤭 🤫 🤥 😶 😐 😑 😬 🙄 😯 😦 😧 😮 😲" +
                                "🥱 😴 🤤 😪 😵 🤐 🥴 🤢 🤮 🤧 😷 🤒 🤕"
                    )
            )
        }
        NotificationType.CUSTOM -> {
            notificationBuilder.setStyle(
                NotificationCompat.DecoratedCustomViewStyle()
            ).setCustomContentView(
                RemoteViews(
                    packageName, R.layout.view_custom_notification
                ).apply {
                    setTextViewText(R.id.title, title)
                    setTextViewText(R.id.message, message)
                }
            )
        }
    }

    return notificationBuilder.build()
}

 

 

반응형

'Android' 카테고리의 다른 글

FCM 메시지가 최신화가 되지 않을때  (0) 2024.06.27
카메라/갤러리 기능  (0) 2023.01.17
Kotlin 문법2 - Class  (0) 2023.01.17
Kotlin 문법1 - 변수/Scope  (0) 2023.01.17
액티비티(Activity)  (0) 2023.01.17