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)
}
)
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 |