깨알 개념/Android

[Android] Notifications - Action 버튼 클릭 시 알림 Cancel

interfacer_han 2024. 6. 14. 03:35

#1 이전 글

[Android] Notifications - PendingIntent

#1 이전 글 [Android] Notifications - 기초#1 개요 알림 개요  |  Views  |  Android Developers이 페이지는 Cloud Translation API를 통해 번역되었습니다. 알림 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준

kenel.tistory.com

위 게시글의 완성된 앱을 일부 수정해서, 알림의 Action 버튼을 클릭하면 해당 알림이 제거(Cancel)되게 만들어본다.

 

#2 PendingIntent.getBroadcast()

Intent는 안드로이드에서 컴포넌트(Activity, Service, BroadcastReceiver, ContentProvider) 간 통신을 위해 사용되는 메시지 객체라는 것을 기억하자. 이전 게시글에선 아래와 같은 방식으로 PendingIntent를 만들었다
 

private fun getThirdActivityAction(): NotificationCompat.Action {
    val intent = Intent(this, ThirdActivity::class.java)
    val pendingIntent: PendingIntent = PendingIntent.getActivity(
        this, myRequestCode2, intent, PendingIntent.FLAG_IMMUTABLE
    )
    return NotificationCompat.Action.Builder(0, "Go Third", pendingIntent).build()
}

Activity를 시작하기 위한 Intent를 만들고, 해당 Intent 객체를 PendingIntent.getActivity()에 넣어 PendingIntent를 완성했었다. 그리고 이 PendingIntent가 Notification의 Action 버튼 클릭 시 실행되게끔 만들었다. 최종적으로, 해당 PendingIntent에 담긴 Intent에 의해 Activity가 호출된다.
 
위 코드를 살짝 바꾸면, PendingIntent에 담긴 Intent가 BroadcastReceiver를 호출하게 만들 수 있다. 아래 코드를 보자.
 

private fun getThirdActivityAction(): NotificationCompat.Action {
    val intent = Intent(this, NotificationReceiver::class.java)
        .putExtra("notificationId", notificationId)
        .putExtra("activityToStart", ThirdActivity::class.java)

    val pendingIntent = PendingIntent.getBroadcast(
        this, myRequestCode2, intent, PendingIntent.FLAG_IMMUTABLE
    )
    return NotificationCompat.Action.Builder(0, "Go Third", pendingIntent).build()
}

Intent 객체를 만들 때, 생성자에 Activity 대신 BroadcastReceiver를 썼다. 그리고 PendingIntent.getActivity() 대신 PendingIntent.getBroadcast()를 사용한다. getActivity()와 getBroadcast() 모두 Context, requestCode, Intent, Flag를 인자로서 요구하며 PendingIntent 객체를 만드는 점에서 동일하나, Intent에 지정된 것이 Activity인지 BroadcastReceiver인지만이 다르다. 한 마디로 전자는 Activity를 시작하기 위한 PendingIntent를 만들고, 후자는 BroadcastReceiver를 시작하기 위한 PendingIntent를 만든다.
 

#3 코드 수정

#3-1 MainActivity.kt 수정

...

class MainActivity : AppCompatActivity() {

    ...
    private val notificationId = 777 // 알림 채널 Id와는 또 다른 알림 하나하나의 Id. 동일한 Id의 알림이 상태바에 있을 때, 같은 Id인 알림을 발생시키면, 원래 있던 알림이 최신 알림으로 갈아끼워진다.

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

    private fun displayNotification() {
        ...
    }

    private fun getSecondActivityPendingIntent(): PendingIntent {
        ...
    }

    private fun getThirdActivityAction(): NotificationCompat.Action {
        val intent = Intent(this, NotificationReceiver::class.java)
            .putExtra("notificationId", notificationId)
            .putExtra("activityToStart", ThirdActivity::class.java)

        val pendingIntent = PendingIntent.getBroadcast(
            this, myRequestCode2, intent, PendingIntent.FLAG_IMMUTABLE
        )
        return NotificationCompat.Action.Builder(0, "Go Third", pendingIntent).build()
    }

    private fun getFourthActivityAction(): NotificationCompat.Action {
        val intent = Intent(this, NotificationReceiver::class.java)
            .putExtra("notificationId", notificationId)
            .putExtra("activityToStart", FourthActivity::class.java)

        val pendingIntent = PendingIntent.getBroadcast(
            this, myRequestCode3, intent, PendingIntent.FLAG_IMMUTABLE
        )
        return NotificationCompat.Action.Builder(0, "Go Fourth", pendingIntent).build()
    }

    ...
}

displayNotification()의 지역 변수였던 notificationId를 밖으로 빼어 그 유효 범위를 MainActivity의 영역까지 넓혔다. 그리고 getThirdActivityAction() 및 getFourthActivityAction()를 #2에 기반하여 수정한다.
 

#3-2 NotificationReceiver.kt 생성

// package com.example.notificationcancel

import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build

class NotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Notification 취소
        val notificationId = intent.getIntExtra("notificationId", -1)
        notificationManager.cancel(notificationId)

        // Activity 실행
        var activityToStart: Class<*>? = null
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            activityToStart = intent.getSerializableExtra("activityToStart", Class::class.java)
        } else {
            activityToStart = intent.getSerializableExtra("activityToStart") as Class<*>
        }

        val activityIntent = Intent(context, activityToStart)
        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(activityIntent)
    }
}

이 클래스는 호출(onReceive())되면, NotificationManager.cancel()로 알림을 제거하고, 새로운 Activity를 시작하는 2가지 작업을 수행한다. getSerializableExtra(name: String)은 API 33부터 Deprecated되었기에 분기(if)문을 넣었다.
 

#3-3 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
        ...>
        <receiver
            android:name=".NotificationReceiver"
            android:enabled="true"
            android:exported="true"></receiver>

        <activity
            android:name=".FourthActivity"
            android:exported="false" />
        <activity
            android:name=".ThirdActivity"
            android:exported="false" />
        <activity
            android:name=".SecondActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

이 작업을 잊으면 안 된다.
 

#4 작동 확인

 

#5 요약

Intent의 역할은 Activity 실행에 국한되지 않는다.
 

#6 완성된 앱

android-practice/notifications/NotificationCancel at master · Kanmanemone/android-practice

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

github.com

'깨알 개념 > Android' 카테고리의 다른 글

[Android] WorkManager - 기초  (0) 2024.06.17
[Android] WorkManager - 개요  (0) 2024.06.14
[Android] Notifications - PendingIntent  (0) 2024.06.12
[Android] Notifications - 기초  (0) 2024.06.12
[Android] Retrofit - Post  (0) 2024.06.11