๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Android] Notifications - ๊ธฐ์ดˆ

interfacer_han 2024. 6. 12. 03:25

#1 ๊ฐœ์š”

 

์•Œ๋ฆผ ๊ฐœ์š”  |  Views  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•Œ๋ฆผ ๊ฐœ์š” ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ์•Œ๋ฆผ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฆฌ๋งˆ์ธ๋”, ๋‹ค๋ฅธ ์‚ฌ๋žŒ๊ณผ

developer.android.com

์•ฑ์—๊ฒŒ ์žˆ์–ด ์•Œ๋ฆผ(Notifications)๋Š” ๋นผ๋†“์„ ์ˆ˜ ์—†๋Š” ๊ตฌ์„ฑ ์š”์†Œ๋‹ค. ์•Œ๋ฆผ์„ ๊ธฐ์ดˆ์ ์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ณธ๋‹ค.

 

#2 SDK, ๊ถŒํ•œ, View ์„ค์ •

#2-1 ์ตœ์†Œ SDK ์„ค์ •

Minimum SDK๋ฅผ API ๊ธฐ์ค€ 26 ์ด์ƒ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค. Notifications ๋Œ€๋ถ€๋ถ„ ๊ธฐ๋Šฅ์˜ ์ตœ์†Œ ์š”๊ตฌ API๊ฐ€ 26์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

#2-2 AndroidManifest.xml์—์„œ ์•Œ๋ฆผ ๊ถŒํ•œ ์„ค์ •

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        ...
    </application>

</manifest>

์•Œ๋ฆผ ๋ฐœ์ƒ์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ํ—ˆ์šฉํ•ด์ค€๋‹ค.

 

#2-3 View ๊ตฌํ˜„

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MainActivity"
        android:textSize="40dp"
        app:layout_constraintBottom_toTopOf="@id/button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Trigger Notification"
        android:textSize="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

TextView ํ•˜๋‚˜์™€ Button ํ•˜๋‚˜๊ฐ€ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ View๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.

 

#3 MainActivity.kt ๊ตฌํ˜„

#3-1 NotificationManager ํ• ๋‹น

// package com.example.notificationsbasics

...

class MainActivity : AppCompatActivity() {

    private var notificationManager: NotificationManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val button = findViewById<Button>(R.id.button)

        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }
}

๊ทธ ๋‹ค์Œ์€ ์•Œ๋ฆผ์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํด๋ž˜์Šค์ธ NotificationManager์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ• ๋‹นํ•œ๋‹ค. NotificationManager๋Š” System Service์˜ ์ผ์ข…์œผ๋กœ, Context.getSystemService(name: String)๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ํ”„๋กœํผํ‹ฐ notificationManager๋ฅผ val์ด ์•„๋‹Œ var๋กœ ์„ ์–ธํ•˜๋Š” ์ด์œ ๋Š”, ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ onCreate()๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋  ์—ฌ์ง€๊ฐ€ ์žˆ๊ณ , ๋”ฐ๋ผ์„œ ๊ทธ ๋•Œ๋งˆ๋‹ค getSystemService(name: String)๊ฐ€ ์žฌํ• ๋‹น๋  ๊ฐ€๋Šฅ์„ฑ ๋˜ํ•œ ์ƒ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

#3-2 NotificationChannel

...

class MainActivity : AppCompatActivity() {

    private var notificationManager: NotificationManager? = null
    private val channelId = "com.example.notificationsbasics.channel1"

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        createNotificationChannel(channelId, "My Channel", "It's my channel")
    }

    private fun createNotificationChannel(id: String, name: String, channelDescription: String) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // ์ˆซ์ž 0์ด ์•„๋‹ˆ๋ผ Oreo์˜ ์ฒซ ๊ธ€์ž์ธ ์•ŒํŒŒ๋ฒณ ๋Œ€๋ฌธ์ž O๋‹ค.
            val importance = NotificationManager.IMPORTANCE_HIGH
            val channel = NotificationChannel(id, name, importance).apply {
                description = channelDescription
            }
            notificationManager?.createNotificationChannel(channel)
        }
    }
}

๋ณธ๊ฒฉ์ ์œผ๋กœ Notifications ์ฝ”๋“œ๋ฅผ ์งค ๋•Œ ์šฐ์„ ์ ์œผ๋กœ ํ•ด์•ผํ•˜๋Š” ์ž‘์—…์€ ๋ฐ”๋กœ NotificationChannel์„ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด๋‹ค. NotificationChannel ๊ฐ์ฒด์˜ ์กด์žฌ ์ด์œ ๋Š” ์•Œ๋ฆผ์˜ ์นดํ…Œ๊ณ ๋ฆฌํ™”๋‹ค. ๋ฉ”์‹ ์ € ์•ฑ์„ ์˜ˆ๋กœ ๋“ค๋ฉด, ๋ฉ”์‹œ์ง€ ๋„์ฐฉ ์•Œ๋ฆผ, ๊ทธ๋ฃน ์ฑ„ํŒ…๋ฐฉ ์ดˆ๋Œ€ ์•Œ๋ฆผ, ์นœ๊ตฌ ์ถ”๊ฐ€ ์•Œ๋ฆผ ๋“ฑ์€ ์„œ๋กœ ๊ตฌ๋ถ„๋˜๋Š” ๋ชฉ์ ๊ณผ ์ค‘์š”๋„๋ฅผ ์ง€๋‹Œ๋‹ค. NotificationChannel์ด ๋ฐ”๋กœ ๊ทธ๋Ÿฌํ•œ ์นดํ…Œ๊ณ ๋ฆฌํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

ํ”„๋กœ๊ทธ๋ž˜๋จธ ์ž…์žฅ์—์„œ ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ๋‚˜๋‰˜๋Š” ์•Œ๋ฆผ๋งˆ๋‹ค ์•Œ๋ฆผ ์Šคํƒ€์ผ, ์ค‘์š”๋„ ๋“ฑ์„ ์ผ๊ด„์ ์œผ๋กœ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์ ์ด ์žˆ๋‹ค. ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋Š” ์ •์ œ๋œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์„ ์‚ฌํ•˜๊ณ , ํŠน์ • ์œ ํ˜•์˜ ์นดํ…Œ๊ณ ๋ฆฌ์— ๋Œ€ํ•œ ์•Œ๋ฆผ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ˆ˜์‹  ๊ฑฐ๋ถ€ํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค.

 

'์‚ฌ์šฉ์ž ์ •์˜ ์•Œ๋ฆผ ์ฑ„๋„'์ด๋ผ๋Š” ๊ฐœ๋… ์ž์ฒด๊ฐ€ API 26๋ถ€ํ„ฐ ์ƒ๊ฒจ๋‚ฌ๊ธฐ์— API 26(์•ˆ๋“œ๋กœ์ด๋“œ Oreo)๋ถ€ํ„ฐ NotificationChannel์ด ์ง€์›๋œ๋‹ค. API๊ฐ€ 26 ๋ฏธ๋งŒ์ธ ์•ˆ๋“œ๋กœ์ด๋“œ OS์—์„œ๋Š” if๋ฌธ์— ์˜ํ•ด createNotificationChannel()๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์œผ๋ฉฐ, ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ •๋œ ์ฑ„๋„์— ์•Œ๋ฆผ์ด ์ˆ˜์‹ ๋œ๋‹ค.

 

#3-3 NotificationManager.notify() ๊ตฌํ˜„

...

class MainActivity : AppCompatActivity() {

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        createNotificationChannel(channelId, "My Channel", "It's my channel")

        button.setOnClickListener {
            displayNotification()
        }
    }

    private fun displayNotification() {
        val notificationId = 777 // ์•Œ๋ฆผ ์ฑ„๋„ Id์™€๋Š” ๋˜ ๋‹ค๋ฅธ ์•Œ๋ฆผ ํ•˜๋‚˜ํ•˜๋‚˜์˜ Id. ๋™์ผํ•œ Id์˜ ์•Œ๋ฆผ์ด ์ƒํƒœ๋ฐ”์— ์žˆ์„ ๋•Œ, ๊ฐ™์€ Id์ธ ์•Œ๋ฆผ์„ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด, ์›๋ž˜ ์žˆ๋˜ ์•Œ๋ฆผ์ด ์ตœ์‹  ์•Œ๋ฆผ์œผ๋กœ ๊ฐˆ์•„๋ผ์›Œ์ง„๋‹ค.
        val notification = NotificationCompat.Builder(this@MainActivity, channelId).apply {
            setContentTitle("My Notification Title") // ์ƒํƒœ๋ฐ” ๋‚ด๋ฆฌ๋ฉด ํ™”๋ฉด์— ๋ณด์ผ ์•Œ๋ฆผ ์ œ๋ชฉ
            setContentText("It's my notification") // ์ƒํƒœ๋ฐ” ๋‚ด๋ฆฌ๋ฉด ํ™”๋ฉด์— ๋ณด์ผ ์•Œ๋ฆผ ๋ณธ๋ฌธ
            setSmallIcon(android.R.drawable.ic_dialog_info) // ์•Œ๋ฆผ ์•„์ด์ฝ˜ ์„ค์ •
            setAutoCancel(true) // ์ƒํƒœ๋ฐ”์—์„œ ์•Œ๋ฆผ ํด๋ฆญ ์‹œ, ์•Œ๋ฆผ์ด ์‚ฌ๋ผ์ง€๊ฒŒ ๋งŒ๋“œ๋Š” ์˜ต์…˜ (ํ•˜์ง€๋งŒ, PendingIntent๊ฐ€ ์—†์œผ๋ฉด ์˜ต์…˜ ์ ์šฉ ์•ˆ๋จ)
            setPriority(NotificationCompat.PRIORITY_HIGH) // API 26 ๋ฏธ๋งŒ์„ ์œ„ํ•œ ์•Œ๋ฆผ ์ค‘์š”๋„ ์˜ต์…˜. API 26 ์ด์ƒ๋ถ€ํ„ฐ๋Š” NotificationChannel์— ํ• ๋‹นํ•œ ์•Œ๋ฆผ ์ค‘์š”๋„๊ฐ€ ์šฐ์„ ์‹œ๋œ๋‹ค.
        }.build()

        notificationManager?.notify(notificationId, notification)
    }

    private fun createNotificationChannel(id: String, name: String, channelDescription: String) {
        ...
    }
}

๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์•Œ๋ฆผ์ด ์ƒํƒœ๋ฐ”์— ๊ฒŒ์‹œ๋˜๋„๋ก ๋งŒ๋“ ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์•Œ๋ฆผ Id๋ฅผ ํ–‰์šด์˜ ์ˆ˜์ธ 777๋กœ ์ •ํ•ด์ฃผ์—ˆ์œผ๋‚˜, ์‹ค์ œ ์•ฑ์„ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ์ฒด๊ณ„์ ์ธ ๊ทœ์น™์„ ์ •ํ•ด๋‘๊ณ  ์‹ ์ค‘ํ•˜๊ฒŒ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. NotificationManager.notify()์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž์ธ Notification์„ AndroidX์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์†ํ•˜๋Š” NotificationCompat์˜ ๋นŒ๋” ํด๋ž˜์Šค NotificationCompat.Builder๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ–ˆ๋‹ค. ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์œผ๋กœ NotificationCompat.Builder ๊ฐ์ฒด๋ฅผ ์—ฐ์‡„์ ์œผ๋กœ ๋งŒ๋“ค๊ณ , ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ build()ํ•˜์—ฌ Notification ๊ฐ์ฒด notification์„ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹ ๊ตฌ๋ฌธ ์ค‘์— ์žˆ๋Š” autoCancel์„ true๋กœ ์„ค์ •ํ•ด๋„, PendingIntent๊ฐ€ ์—†๋‹ค๋ฉด ์ƒํƒœ๋ฐ”์—์„œ ์•Œ๋ฆผ์„ ํด๋ฆญํ•ด๋„ ์•Œ๋ฆผ์ด ์‚ฌ๋ผ์ง€์ง€ ์•Š๋Š”๋‹ค. ๋‹ค์Œ ๊ฒŒ์‹œ๊ธ€์—์„œ NotificationCompat.Builder.setContentIntent(intent: PendingIntent?)๋ฅผ ํ†ตํ•ด PendingIntent๋ฅผ ํ• ๋‹นํ•ด๋ณด๊ฒ ๋‹ค.

 

#4 ์ž‘๋™ ํ™•์ธ

๋งŒ์•ฝ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ ์•Œ๋ฆผ์ด ๋œจ์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์„ค์น˜๋œ ์•ฑ์˜ ์•Œ๋ฆผ ๊ถŒํ•œ์ด ์ฐจ๋‹จ๋˜์ง€๋Š” ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž. ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •์ด ์ฐจ๋‹จ์ธ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

 

#5 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/notifications/NotificationsBasics at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com

 

#6 ์ด์–ด์ง€๋Š” ๊ธ€

 

[Android] Notifications - PendingIntent

#1 ์ด์ „ ๊ธ€ [Android] Notifications - ๊ธฐ์ดˆ#1 ๊ฐœ์š” ์•Œ๋ฆผ ๊ฐœ์š”  |  Views  |  Android Developers์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•Œ๋ฆผ ๊ฐœ์š” ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€

kenel.tistory.com

๋ณธ ๊ฒŒ์‹œ๊ธ€์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์•Œ๋ฆผ ํด๋ฆญ ์‹œ์˜ ๋™์ž‘์„ ๊ตฌํ˜„ํ•ด๋ณธ๋‹ค.