Android

[Android/Kotlin] Notification 알림 스타일 종류 및 실행 코드

haehyun 2022. 1. 6. 15:52
728x90

Notification

앱에서 보내는 정보를 제공하기 위해 Android가 앱의 UI 외부에 표시하는 메시지. 즉, 스마트폰 기기의 상태 바 영역에 표시되는 푸시알림을 일컫는다. 사용자는 알림을 탭하여 해당 앱을 열거나 알림창에서 바로 특정 작업을 수행할 수 있다. (ex: 카톡 알림 클릭 시 카카오톡 채팅방으로 이동 or 카톡 알림에서 바로 답장하기)

※ 알림 띄우기 과정 Notification API에 관한 내용은 이전 게시글을 참고
2022.01.05 - [Android] - [Android] 상태바 알림(Notification), 채널(Channel)

  • 모든 알림의 activity_main.xml 파일(레이아웃)은 동일, 메인화면의 "TEST" 버튼을 클릭하며 알림이 뜬다.
  • 알림 스타일에는 어떤 것이 있는지, 어떤 알림 형태를 기본 제공하는지만을 알아볼 것이기 때문에 알림 터치 이벤트를 구현하지 않음. (ex: 알림을 클릭해도 해당 앱이 열리지 않음)
  • 알림 터치 이벤트, 액션 터치 이벤트를 구현하지 않음. Broadcast Receiver도 더미 파일
  • 각 알림 스타일의 세부 내용(메서드 기능)은 설명하지 않음 => 다음에 별도로 하나씩 다룰 예정
  • 모든 예제는 뷰 객체(버튼)는 뷰바인딩(View Binding) 기능으로 접근하기 때문에 gradle 파일 수정 필수!
  • NotificationChannel ~ Notification객체 생성 까지의 코드는 중복되는 부분이나, 생략하지 않고 코드를 통으로 가져옴.

 

기본 알림

스몰아이콘, 알림 시각, 타이틀명, 내용을 표시한다. 알림 터치 이벤트를 지정할 수 있다.

 

[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Notification Test"
        android:layout_centerHorizontal="true"
        android:layout_above="@+id/button"
        android:textSize="30sp"
        android:layout_marginBottom="20dp"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TEST"
        android:layout_centerInParent="true"/>

</RelativeLayout>

 

[MainActivity.kt]

  • 레이아웃 뷰 객체 접근을 뷰바인딩 방법을 사용함
  • 버튼 객체에 클릭 이벤트 핸들러 등록
package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 start
            val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            val builder: NotificationCompat.Builder

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // 채널 생성
                val channelId = "one-channel"
                val channelName = "My Channel One"
                val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)

                // 채널 정보 설정
                channel.description = "My Channel One Description"      // 채널 설명
                channel.setShowBadge(true)                              // 배지 표시
                val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
                val audioAttributes = AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .build()
                channel.setSound(uri, audioAttributes)                      // 알림음 재생
                channel.enableLights(true)                              // 불빛 표시
                channel.lightColor = Color.RED                              // 불빛 색상
                channel.enableVibration(true)                       // 진동 울림
                channel.vibrationPattern = longArrayOf(100, 200, 100, 200)  // 진동 패턴

                // 채널을 NotificationManager에 등록
                manager.createNotificationChannel(channel)

                // 채널 Id로 NotificationCompat.builder 생성
                builder = NotificationCompat.Builder(this, channelId)
            } else {
                builder = NotificationCompat.Builder(this)
            }
            // Notification 중복 부분 end (뒤 예제들에서부터 해당 부분 코드는 생략) 

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_notification_overlay)    // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("Content Title")        // 타이틀
            builder.setContentText("Content Massage")       // 내용

            manager.notify(11, builder.build())
        }
    }
}

 

Action

일반 알림 내용에 더불어 최대 3개까지 Action을 추가할 수 있다. 각 Action에 대해 터치 이벤트를 지정할 수 있다.

 

[MainActivity.kt]

package com.example.androidlab

import ...

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~~~~~~~
            
            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_notification_overlay)    // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("Content Title")        // 타이틀
            builder.setContentText("Content Massage")       // 내용
            
            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.sym_action_call)    // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("전화")        // 타이틀
            builder.setContentText("김철수님에게 전화가 왔습니다.")       // 내용

            // 액션 등록
            val actionIntent = Intent(this, ReplyReceiver::class.java)
            val actionPendingIntent = PendingIntent.getBroadcast(this, 20, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.sym_action_call,
                    "전화 받기",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.stat_notify_missed_call,
                    "전화 거절",
                    actionPendingIntent
                ).build()
            )

            manager.notify(11, builder.build())
        }
    }
}

 

RemoteInput

알림에서 사용자 입력을 직접 받는 기법. 앱의 화면(EditText가 존재하는 화면)을 통하지 않고 원격으로 액션에서 직접 입력을 받아서 처리할 수 있다. (ex: 문자, 메신저 답장 기능)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~~~~~~~
            
            // 알림 정보 설정
            builder.setSmallIcon(R.drawable.small)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("김철수")                  // 타이틀
            builder.setContentText("미안한데, 돈 좀 빌려줄 수 있을까?")      // 내용

            // 원격 입력 정보(식별자, 힌트 문자열) 설정, RemoteInput 객체 생성
            val KEY_TEXT_REPLY = "key_text_reply"
            val replyLabel: String = "답장"
            val remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
                setLabel(replyLabel)
                build()
            }

            // 액션 터치 이벤트 처리 PendingIntent
            val replyIntent = Intent(this, ReplyReceiver::class.java)
            val replyPendingIntent = PendingIntent.getBroadcast(this, 30, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)

            // 액션 등록
            builder.addAction(
                NotificationCompat.Action.Builder(
                    R.drawable.send,
                    "답장",
                    replyPendingIntent
                ).addRemoteInput(remoteInput).build()
            )

            manager.notify(11, builder.build())
        }
   }
}

 

Progress

앱에서 이루어지는 작업의 진행 상황을 알림에 프로그레스바로 표시할 때 사용한다.
(ex: 파일 다운로드, 파일 업로드, 동기화 등 0~100 퍼센트(%)로 경과를 표시할 수 있는 작업)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import com.example.androidlab.databinding.ActivityMainBinding
import kotlin.concurrent.thread

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~~~~~~~

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_menu_upload_you_tube)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("영상 업로드")                  // 타이틀
            builder.setContentText("옵션을 보려면 누르세요")      // 내용

            // 액션 2개 추가
            val actionIntent = Intent(this, ReplyReceiver::class.java)
            val actionPendingIntent = PendingIntent.getBroadcast(this, 20, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_media_pause,
                    "일시중지",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_menu_close_clear_cancel,
                    "취소",
                    actionPendingIntent
                ).build()
            )

            // 프로그레스 설정
            builder.setProgress(100, 0, false)
            manager.notify(11, builder.build())
            
            // 1~100 진행값 변경 스레드
            thread {
                for (i in 1..100) {
                    builder.setProgress(100, i, false)
                    manager.notify(11, builder.build())
                    SystemClock.sleep(100)
                }
            }
        }
   }
}

 

BigPictureStyle

알림에 큰 이미지를 출력할 때 사용한다.
(ex: 스크린샷 캡처, 이미지 다운로드 시 이미지 출력)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_menu_gallery)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("스크린샷 저장됨")                  // 타이틀
            builder.setContentText("갤러리에서 열려면 여기를 누르세요.")      // 내용

            // 액션 3개 추가
            val actionIntent = Intent(this, ReplyReceiver::class.java)
            val actionPendingIntent = PendingIntent.getBroadcast(this, 20, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_media_pause,
                    "공유",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_menu_close_clear_cancel,
                    "편집",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_menu_close_clear_cancel,
                    "삭제",
                    actionPendingIntent
                ).build()
            )
            
            // BigPictureStyle 적용
            val bigPicture = BitmapFactory.decodeResource(resources, R.drawable.lake)
            val bigStyle = NotificationCompat.BigPictureStyle()
            bigStyle.bigPicture(bigPicture)
            builder.setStyle(bigStyle)

            manager.notify(11, builder.build())
        }
   }
}

 

BigTextStyle

알림에 긴 문자열을 출력해 사용자가 앱을 실행하지 않아도 많은 정보를 알 수 있도록 한다.
(ex: 문자, 메신저, 이메일 내용 출력)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_dialog_email)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("김철수")                  // 타이틀
            builder.setContentText("애국가 가사 보냅니다.")      // 내용

            // 액션 3개 추가
            val actionIntent = Intent(this, ReplyReceiver::class.java)
            val actionPendingIntent = PendingIntent.getBroadcast(this, 20, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_media_pause,
                    "보관",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_menu_close_clear_cancel,
                    "삭제",
                    actionPendingIntent
                ).build()
            )
            builder.addAction(
                NotificationCompat.Action.Builder(
                    android.R.drawable.ic_menu_close_clear_cancel,
                    "회신",
                    actionPendingIntent
                ).build()
            )
            
            // BigTextStyle 적용
            val bigTextStyle = NotificationCompat.BigTextStyle()
            bigTextStyle.bigText(resources.getString(R.string.long_text))
            builder.setStyle(bigTextStyle)

            manager.notify(11, builder.build())
        }
   }
}

 

InboxStyle

문자열을 목록으로 출력할 때 사용한다. 하나의 알림에 문자열을 여러 개 나열할 수 있다. (문자열을 라인별로 구분)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.ic_dialog_email)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("3개의 새로운 메일")                  // 타이틀
            builder.setContentText("김철수, 이영희, 네이버쇼핑")      // 내용
            
            // InBoxStyle 적용
            val inBoxStyle = NotificationCompat.InboxStyle()
            inBoxStyle.addLine("김철수 : 좋은 아침입니다.")
            inBoxStyle.addLine("이영희 : 안녕하십니까. 00기업 영업팀 이영희입니다.")
            inBoxStyle.addLine("네이버쇼핑 : 주문하신 상품이 배송완료 되었습니다.")
            builder.setStyle(inBoxStyle)

            manager.notify(11, builder.build())
        }
   }
}

 

Message

여러 사람이 주고받은 메시지를 구분해서 출력할 때 사용한다. 표시할 메시지는 각각 하나의 Message객체이며 메시지 내용, 메시지가 발생한 시각, 메시지 발신인(Person객체)에 대한 정보를 가지고 있다.
(ex: Teams등 협업 프로그램 댓글, 회의실, 채팅방 등)

 

package com.example.androidlab

import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.graphics.drawable.IconCompat
import com.example.androidlab.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // Notification 중복 부분 생략~~~~~~~~~~~~~~~~~~

            // 알림 정보 설정
            builder.setSmallIcon(android.R.drawable.sym_action_chat)          // 스몰 아이콘
            builder.setWhen(System.currentTimeMillis())     // 알림 시각
            builder.setContentTitle("채팅")                  // 타이틀
            builder.setContentText("프로그래밍 스터디")      // 내용

            // Person 객체 생성 (발신인 이름, 아이콘)
            val sender1: Person = Person.Builder()
                .setName("김철수")
                .setIcon(IconCompat.createWithResource(this, R.drawable.user))
                .build()

            val sender2: Person = Person.Builder()
                .setName("이영희")
                .setIcon(IconCompat.createWithResource(this, R.drawable.user))
                .build()

            // Message 객체 생성 (내용, 시간)
            val message1 = NotificationCompat.MessagingStyle.Message(
                "안녕하세요. 김철수입니다.",
                System.currentTimeMillis(),
                sender1
            )
            val message2 = NotificationCompat.MessagingStyle.Message(
                "이영희입니다. 잘 부탁드립니다.",
                System.currentTimeMillis(),
                sender2
            )

            // MessageStyle 적용
            val messageStyle = NotificationCompat.MessagingStyle(sender1)
                .addMessage(message1)
                .addMessage(message2)
            builder.setStyle(messageStyle)

            manager.notify(11, builder.build())
        }
   }
}

 

아직 Notification에 능숙하지 못해서 예시가 잘못된 부분이 있을 수 있다. 알림 기능에 대해 좀 더 학습하면서 해당 게시글의 내용을 보강해 나갈 예정이다.


참고자료

728x90