이 Codelab은 Kotlin 기반 Android 고급 교육 과정의 일부입니다. Codelab을 순서대로 진행하는 경우 학습 효과를 극대화할 수 있지만 순서를 바꿔 진행해도 괜찮습니다. 모든 교육 과정 Codelab은 Kotlin 기반 고급 Android Codelab 방문 페이지에 나열되어 있습니다.
소개
알림은 앱의 UI 외부에 사용자에게 표시되는 메시지입니다. 기기가 잠금 해제되어 있으면 화면 상단에 알림이 표시되고, 기기가 잠겨 있으면 보안 설정에 따라 잠금 화면에 알림이 표시됩니다.
일반적인 알림은 제목, 설명, 아이콘으로 구성됩니다. 알림에는 클릭 가능한 작업, 빠른 답장, 확장 가능한 콘텐츠, 이미지도 포함될 수 있습니다.
알림은 적시에 자료를 제공할 수 있으며, 사용자가 답장을 보내거나 알람을 일시중지하는 등의 빠른 작업을 실행할 수 있는 버튼이 있을 수 있습니다. 알림을 클릭하면 사용자가 알림 콘텐츠와 관련된 앱의 보기로 이동합니다.
알림은 앱이 백그라운드에 있는 동안 사용자에게 중요한 작업을 상기시키거나, 어떤 일이 발생했음을 알리거나, 즉시 필요한 중요한 정보를 전달하는 데 유용합니다. 알림은 가급적 사용하지 마세요. 이렇게 하면 사용자를 존중할 수 있을 뿐만 아니라 앱의 알림이 주목받을 가능성도 높아집니다.
이 Codelab에서는 Android 앱에서 알림을 만들고 사용하는 방법을 알아봅니다.
기본 요건
다음을 잘 알고 있어야 합니다.
- Kotlin으로 Android 앱을 만드는 방법 특히 Android SDK를 사용합니다.
- 아키텍처 구성요소와 데이터 결합을 사용하여 앱을 설계하는 방법
- BroadcastReceiver에 관한 기본 이해
- AlarmManager에 관한 기본적인 이해
학습할 내용
- 알림을 만들고 스타일을 지정하고 전송하는 방법
- 알림을 취소하는 방법
- 알림 채널을 만드는 방법
- 알림에 빠른 작업을 추가하는 방법
- 앱 아이콘에 알림 배지를 표시하는 방법입니다.
실습할 내용
- 시작 앱에 알림을 추가합니다.
- 이전에 보낸 알림을 취소합니다.
- 다양한 유형의 알림을 위한 채널을 만듭니다.
- 스타터 앱에서 알림을 맞춤설정합니다.
- 빠른 작업을 추가하여 알림을 대화형으로 만드세요.
- 알림 배지를 사용 중지합니다.
달걀 요리는 간단하지만 시간을 추적하지 않으면 어려울 수 있습니다. 이 Codelab에서는 달걀 타이머 앱을 작업하여 미래의 달걀처럼 완벽하게 만듭니다. 사용자가 다양한 달걀 스타일에 따라 다른 조리 시간 설정을 지정할 수 있는 작동하는 달걀 타이머 앱으로 시작합니다. 타이머는 선택한 시간 간격부터 카운트다운하며 달걀이 준비되면 토스트 메시지를 표시합니다.
기능적으로는 문제가 없어 보이지만 완벽하지 않고 사용자 친화적이지도 않습니다. 우선 토스트 메시지는 짧은 시간 동안만 표시되므로 놓치기 쉽습니다. 또한 앱이 포그라운드에 있지 않거나 기기가 잠겨 있으면 토스트 메시지가 사라진 후 타이머 상태에 대한 시각적 표시기가 없습니다.
이상적으로는 타이머가 알림을 사용하여 사용자에게 시간이 끝났음을 알려야 합니다. 사용자는 달걀이 바로 준비되었는지 알아야 합니다. 그렇지 않으면 달걀이 너무 익을 수 있습니다. 알림은 시각적이며 소리를 포함할 수 있고 기기를 진동시킬 수 있습니다. 모두 사용자의 관심을 끌기 위한 방법입니다. 이렇게 하면 완벽한 달걀과 만족스럽고 배부른 사용자를 만들 수 있습니다.
샘플 앱을 가져오려면 다음 중 하나를 실행하세요.
GitHub에서 저장소를 클론하고 starter 브랜치로 전환합니다.
$ git clone https://github.com/googlecodelabs/android-kotlin-notifications
또는 ZIP 파일로 저장소를 다운로드한 다음 압축을 풀고 Android 스튜디오에서 열어도 됩니다.
- Android 스튜디오에서 앱을 열고 실행합니다.
달걀 이미지와 달걀을 조리할 수 있는 사전 정의된 시간 간격 목록이 있는 드롭다운 메뉴가 표시됩니다. 반숙 드롭다운 메뉴의 삼각형을 클릭합니다. 목록의 첫 번째 옵션은 테스트용으로 제공되며 알람을 10초로 설정합니다. 목록 옆에는 타이머를 시작하는 스위치가 있습니다. 이 스위치를 사용하면 언제든지 타이머를 시작하고 중지할 수 있습니다. 시작 코드는 완전히 작동하므로 타이머를 설정하고 0까지 카운트다운되는 것을 확인할 수 있습니다. 타이머가 완료되면 아래와 같이 토스트 메시지가 표시됩니다.
- 소스 코드를 검사합니다. 시작 앱은
MainActivity
라는 단일 활동으로 구성됩니다.receiver
,ui
,util
라는 세 개의 하위 패키지가 있습니다.
- /receiver—
receiver
패키지에는AlarmReceiver
및SnoozeReceiver
라는 브로드캐스트 리시버가 두 개 포함되어 있습니다.AlarmReceiver
은AlarmManager
에 의해 트리거되어 사용자 정의 타이머가 만료되면 알림을 보냅니다.SnoozeReceiver
는 사용자가 알림을 일시 중지하기 위해 클릭하는 것을 처리합니다. - /ui - 앱의 UI 부분에 속하는
EggTimerFragment
가 포함되어 있습니다.EggTimerViewModel
는 타이머를 시작하고 취소하며 기타 수명 주기 관련 앱 작업을 담당합니다. - /util - 이 패키지에는 두 개의 파일이 있습니다.
BindingUtils.kt
에는 앱 UI와ViewModel
간의 데이터 결합을 지원하는 결합 어댑터가 있습니다.NotificationUtils.kt
에는NotificationManager
의 확장 프로그램 메서드가 있습니다.
알림을 사용하면 사용자의 관심을 앱으로 유도할 수 있습니다. 앱이 실행되지 않거나 포그라운드에서 실행되는 경우 알림은 화면 상단에 팝업 창을 표시하며 소리나 진동을 포함할 수 있습니다. 알림을 만들려면 알림 빌더를 사용하고 제목 텍스트, 콘텐츠 텍스트, 아이콘을 제공해야 합니다. 빌더에 필요한 필드가 모두 있으면 시스템 서비스인 NotificationManager
를 사용하여 이 콘텐츠를 알림으로 표시할 수 있습니다. NotificationManager
는 알림을 전송하고, 콘텐츠를 업데이트하고, 알림을 취소할 책임이 있습니다. 다음 단계에서는 NotificationManager
에 확장 메서드를 추가합니다. 이렇게 하면 NotificationManager
를 사용해야 할 때마다 이러한 확장 함수를 사용하여 필요한 기능을 사용할 수 있습니다.
1단계: 기본 알림 만들기
이 작업에서는 새 알림을 만들고, 사용자에게 메시지를 설정하고, 알림을 전송합니다.
NotificationUtils.kt
클래스를 열고TODO: Step 1.1
을 찾습니다. 이 Codelab과 앱 코드에서 일치하는 할 일을 확인할 수 있습니다.- 제공된
sendNotification()
함수를 검사합니다. 이 확장 프로그램 함수를NotificationManager
로 확장하여 알림을 전송합니다.
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
* Builds and delivers a notification.
*
* @param messageBody, notification text.
* @param context, activity context.
*/
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
- 알림 빌더의 인스턴스를 가져와 앱 컨텍스트와 채널 ID를 전달합니다. 채널 ID는 채널의 문자열 값입니다.
알림 채널은 알림을 그룹화하는 방법입니다. 유사한 유형의 알림을 그룹화하면 개발자와 사용자가 채널의 모든 알림을 제어할 수 있습니다. 채널이 생성되면 원하는 수의 알림을 전송하는 데 사용할 수 있습니다.
//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
applicationContext,
applicationContext.getString(R.string.egg_notification_channel_id)
)
- 알림 아이콘을 앱을 나타내도록 설정하고, 사용자에게 제공할 메시지의 제목과 콘텐츠 텍스트를 설정합니다. Codelab에서는 알림을 더 맞춤설정할 수 있는 옵션이 더 많이 표시되지만, 알림을 보내려면 최소한 이 정도의 데이터를 설정해야 합니다.
//NotificationUtils.kt
// TODO: Step 1.3 set title, text and icon to builder
.setSmallIcon(R.drawable.cooked_egg)
.setContentTitle(applicationContext.getString(R.string.notification_title))
.setContentText(messageBody)
- 그런 다음 알림의 고유 ID와 빌더의
Notification
객체를 사용하여notify()
를 호출해야 합니다.
이 ID는 현재 알림 인스턴스를 나타내며 이 알림을 업데이트하거나 취소하는 데 필요합니다. 앱에는 특정 시점에 활성 알림이 하나만 있으므로 모든 알림에 동일한 ID를 사용할 수 있습니다. NotificationUtils.kt
에는 이러한 목적으로 NOTIFICATION_ID
라는 상수가 이미 제공되어 있습니다. 동일한 클래스의 확장 함수에서 호출을 실행하므로 notify()
를 직접 호출할 수 있습니다.
//NotificationUtils.kt
// TODO: Step 1.4 call notify to send the notification
// Deliver the notification
notify(NOTIFICATION_ID, builder.build())
ui/EggTimerViewModel.kt
를 열고startTimer()
함수를 찾습니다. 이 함수는 사용자가 타이머를 사용 설정하면 선택된 시간 간격으로 알람을 만듭니다.- 사용자가 타이머를 시작하면 이 함수에서 알림을 트리거합니다. 이전에 구현한
sendNotification()
함수를 호출하려면NotificationManager
인스턴스가 필요합니다.NotificationManager
는 추가한 확장 함수를 비롯해 알림 API에 노출된 모든 기능을 제공하는 시스템 서비스입니다. 알림을 전송, 취소 또는 업데이트하려면 언제든지 시스템에서NotificationManager
인스턴스를 요청해야 합니다. 알림 메시지와 컨텍스트를 사용하여sendNotification()|
함수를 호출합니다.
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager
// and call sendNotification
val notificationManager = ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(app.getString(R.string.timer_running), app)
거의 완료되었습니다. 하지만 지금 앱을 실행하고 타이머를 설정하면 알림이 표시되지 않습니다.
logcat
를 열고"No Channel found"
을 검색합니다.egg_channel
가 존재하지 않는다는 오류 메시지가 표시됩니다. 다음 단계에서 알림 채널에 대해 자세히 알아보고 이 문제를 해결합니다.
2단계: 알림 채널
API 수준 26부터는 모든 알림을 채널에 할당해야 합니다. 앱 런처 아이콘을 길게 탭하고 앱 정보를 선택한 다음 알림을 탭하면 앱과 연결된 알림 채널 목록이 표시됩니다. 앱에서 채널을 만들지 않았으므로 현재 목록은 비어 있습니다.
채널은 알림의 '유형'을 나타냅니다. 예를 들어 달걀 타이머는 달걀이 익으면 알림을 보낼 수 있으며, 다른 채널을 사용하여 아침 식사로 달걀을 먹도록 매일 알림을 보낼 수도 있습니다. 채널의 모든 알림이 그룹으로 묶이며 사용자는 전체 채널의 알림 설정을 구성할 수 있습니다. 이를 통해 사용자는 관심 있는 알림 종류에 따라 알림 설정을 맞춤설정할 수 있습니다. 예를 들어 사용자는 아침 식사 알림을 사용 중지할 수 있지만 타이머의 알림은 계속 표시하도록 선택할 수 있습니다.
개발자는 채널의 모든 알림에 적용할 초기 설정, 중요도, 동작을 설정합니다. 초기 설정을 완료한 후 사용자는 이러한 설정을 재정의할 수 있습니다.
1.1단계에서 egg_notification_channel_id
을 알림 채널로 사용했으므로 이제 이 채널의 알림 설정과 동작을 실제로 만들고 맞춤설정해야 합니다.
EggTimerFragment.kt
를 열고createChannel()
함수를 찾습니다.- 고유 채널 ID를
NotificationChannel
생성자에 전달합니다. - 사용자가 설정 화면에서도 볼 수 있는 알림 채널 이름을 전달합니다.
- 마지막 매개변수로 알림 채널의 중요도 수준을 전달합니다. 중요도 수준은 이 Codelab의 후반부에서 다루므로 지금은
NotificationManag
er.IMPORTANCE_LOW
를 사용하면 됩니다. notificationChannel
객체에서enableLights
을 true로 설정합니다. 이 설정은 알림이 표시될 때 조명을 사용 설정합니다.- 알림이 표시될 때 빨간색 표시등이 표시되도록
notificationChannel
객체에서lightColor
을 빨간색으로 설정합니다. notificationChannel
객체에서enableVibration
을 true로 설정하여 진동을 사용 설정합니다.notificationChannel
객체에서 채널 설명을‘Time for breakf
ast'로 설정합니다.getSystemService()
을 호출하여NotificationManager
인스턴스를 가져옵니다.-
NotificationManager
에서createNotificationChannel()
를 호출하고 이전 단계에서 만든notificationChannel
객체를 전달합니다.
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
// TODO: Step 1.6 START create a channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_LOW
)
// TODO: Step 2.6 disable badges for this channel
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.enableVibration(true)
notificationChannel.description = "Time for breakfast"
val notificationManager = requireActivity().getSystemService(
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
}
// TODO: Step 1.6 END create channel
}
- 다음으로 채널을 만들려면 방금 작성한
createChannel()
함수 (1.7단계)를 호출해야 합니다. 이 함수는 채널 ID와 채널 이름이라는 두 개의 매개변수를 사용합니다. 프로젝트에 이미 제공된 문자열 리소스에서 채널 ID와 채널 이름을 조회해야 합니다.
// EggTimerFragment.kt
// TODO: Step 1.7 call createChannel
createChannel(
getString(R.string.egg_notification_channel_id),
getString(R.string.egg_notification_channel_name)
)
- 채널 ID를 알림 빌더에 전달해야 합니다. 1.2단계에서 이미 완료했습니다. 잘못된 값을 채널 ID로 설정하면 알림이 실패합니다.
NotificationUtils.kt
를 열어 이전에 설정한 채널 ID가 올바른지 확인합니다.
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
applicationContext,
// TODO: Step 1.8 verify the notification channel name
applicationContext.getString(R.string.egg_notification_channel_id)
)
- 앱을 실행하면 타이머를 시작할 때마다 앱에서 알림을 전송하는 것을 확인할 수 있습니다.
- 상태 표시줄을 풀하고 알림 제목, 콘텐츠, 아이콘이 이전 단계에서 설정한 대로 되어 있는지 확인합니다.
- 새로 만든 채널을 확인하려면 앱을 닫고 앱 아이콘을 찾습니다. 앱 아이콘을 길게 탭하고 앱 정보를 선택합니다.
- 설정 목록에서 알림을 선택합니다. 알림 표시 설정 바로 아래에 Egg라는 새 채널이 표시됩니다.
이제 앱을 실행하면 알림이 표시됩니다. 앱 개발자와 사용자는 모두 이 채널에서 전송되는 모든 알림의 설정과 동작을 맞춤설정할 수 있습니다. 알림을 만들었습니다.
3단계: 앱에 알림 추가
지금까지는 알림 API의 기본 사용법을 보여주었지만 타이머를 시작한 직후에 알림을 전송하는 것은 의미가 없습니다. 사용자는 알이 준비되면 알림을 받는 것을 선호할 것입니다. Codelab의 다음 부분에서는 이 문제를 수정하고 토스트 메시지를 알림으로 변경합니다.
이미 알림을 전송하고 사용자에게 표시되는 방식을 확인했지만 이는 훌륭한 알림을 만들기 위한 첫 번째 단계일 뿐입니다. 이 단계에서는 알림이 더 적절한 시간에 전송되도록 변경합니다.
앱이 AlarmManager
를 사용하여 알람을 설정합니다. AlarmManager
와 관련된 코드는 시작 코드에 이미 제공되어 있으며 토스트 메시지를 표시하는 데 사용됩니다. AlarmManager
는 원하는 시간 선택을 추적하고 시간이 되면 AlarmReceiver.kt
의 onReceive()
함수를 트리거합니다. AlarmReceiver.kt
를 열고 onReceive()
로 이동하면 타이머를 설정할 때마다 표시되는 토스트 메시지가 표시됩니다.
AlarmReceiver.kt
(NotificationManager
인스턴스)를 열고 메시지 텍스트와 컨텍스트 매개변수를 사용하여sendNotification()
함수를 호출합니다.
// AlarmReceiver.kt
// TODO: Step 1.9 add call to sendNotification
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(
context.getText(R.string.eggs_ready).toString(),
context
)
- 선택적으로 타이머가 종료되면 앱에서 알림을 전송하므로 토스트를 삭제합니다.
// AlarmReceiver.kt
// TODO: Step 1.10 [Optional] remove toast
// Toast.makeText(
// context,
// context.getText(R.string.eggs_ready),
// Toast.LENGTH_SHORT
// ).show()
- 앱을 실행합니다 . 타이머를 시작할 때마다, 타이머가 종료될 때마다 알림이 표시됩니다.
이는 이상적이지 않습니다. 사용자에게 너무 많은 알림을 보내지 않아야 합니다. 사용자가 타이머를 시작할 때 전송되는 첫 번째 알림을 삭제할 수 있습니다.
EggTimerFragment.kt
을 열고 1.5단계의 알림 코드를 삭제합니다.
// EggTimeViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
// app,
// NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
- 앱을 다시 실행합니다.
- 타이머를 설정하고 백그라운드로 전환한 후 시간이 종료될 때까지 기다립니다. 알림이 표시됩니다. 훨씬 더 유용한 알림입니다.
4단계: 콘텐츠 인텐트 추가
- 앱이 아직 실행되고 있지 않다면 앱을 다시 실행합니다.
- 알림을 클릭합니다. 아무 일도 일어나지 않습니다.
알림을 표시하고 사용자에게 알리는 것은 좋지만 사용자가 알림을 클릭하면 해당 앱으로 돌아갈 것으로 예상합니다. Codelab의 이 부분에서는 사용자를 타이머 화면으로 다시 데려오기 위해 알림에 인텐트를 추가합니다.
Intent
는 다른 앱 구성요소에서 작업을 요청하는 데 사용할 수 있는 메시지 객체입니다. 인텐트는 활동이나 서비스를 시작하거나 브로드캐스트를 전송하는 데 사용할 수 있습니다. 이 경우 이 인텐트를 사용하여 사용자가 알림을 탭할 때 MainActivity
를 열도록 시스템에 지시합니다. 앱이 단일 보기로만 구성되어 있으므로 여기에는 옵션이 많지 않습니다. 하지만 더 큰 앱에서는 사용자가 알림과 상호작용할 때 적합한 화면으로 사용자를 안내하여 원활한 환경을 제공해야 합니다.
NotificationUtils.kt
를 열고sendNotification()
확장 함수를 찾습니다.applicationContext
및 실행할 활동MainActivity::class.java
로Intent
를 만듭니다.
// NotificationUtils.kt
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
// Create the content intent for the notification, which launches
// this activity
// TODO: Step 1.11 create intent
val contentIntent = Intent(applicationContext, MainActivity::class.java)
인텐트를 만들었지만 알림이 앱 외부에 표시됩니다. 앱 외부에서 인텐트가 작동하도록 하려면 새 PendingIntent
를 만들어야 합니다.
PendingIntent
는 다른 애플리케이션이나 시스템이 애플리케이션을 대신하여 작업을 실행할 권한을 부여합니다. PendingIntent
자체는 단순히 시스템에서 유지관리하는 토큰의 참조이며, 이 토큰은 데이터를 가져오는 데 사용된 원본 데이터를 설명합니다. 즉, 소유 애플리케이션의 프로세스가 종료되더라도 PendingIntent
자체는 부여된 다른 프로세스에서 계속 사용할 수 있습니다. 이 경우 타이머 앱이 실행 중인지 여부와 관계없이 시스템은 대기 중인 인텐트를 사용하여 사용자를 대신해 앱을 엽니다.
applicationContext
,NOTIFICATION_ID
, 이전 단계에서 만든contentIntent
,PendingIntent
플래그를 사용하여PendingIntent
를 만듭니다.PendingIntent
플래그는 새PendingIntent
를 만들거나 기존PendingIntent
를 사용하는 옵션을 지정합니다. 기존 알림이 있는 경우 새 알림을 만들지 않으므로PendingIntent.FLAG_UPDATE_CURRENT
을 플래그로 설정해야 합니다. 이렇게 하면 제공하는 인텐트와 연결된 현재PendingIntent
가 수정됩니다.
// NotificationUtils.kt
// TODO: Step 1.12 create PendingIntent
val contentPendingIntent = PendingIntent.getActivity(
applicationContext,
NOTIFICATION_ID,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
PendingIntent
를 알림에 전달합니다.NotificationBuilder
에서setContentIntent()
를 호출하면 됩니다. 이제 알림을 클릭하면PendingIntent
가 트리거되어MainActivity
이 열립니다.- 또한 사용자가 알림을 탭하면 앱으로 이동하면서 알림이 자동으로 닫히도록
setAutoCancel()
을true
로 설정합니다.
// NotificationUtils.kt
// TODO: Step 1.13 set content intent
.setContentIntent(contentPendingIntent)
.setAutoCancel(true)
- 앱을 다시 실행합니다.
- 타이머를 설정하고 앱을 백그라운드에 배치한 후 알림이 표시될 때까지 기다립니다.
- 알림이 표시되면 상태 표시줄을 아래로 드래그하여 알림을 클릭하고 앱이 포그라운드로 전환되는 방식을 확인합니다.
5단계: 알림 취소하기
알림 기능이 있는 타이머가 있지만 작은 문제가 있습니다. 타이머를 설정하고 알림을 받은 후 타이머를 다시 설정하면 새 타이머가 실행되는 동안 이전 알림이 상태 표시줄에 표시됩니다. 앱이 백그라운드에 있으면 사용자에게 혼란을 줄 수 있으며, 달걀이 덜 익을 수 있습니다.
이 문제를 해결하려면 새 타이머를 시작할 때 이전 알림을 삭제해야 합니다. 먼저 NotificationUtils.kt
에서 다른 확장 함수를 만듭니다. NotificationManager
에는 cancelAll
()
라는 활성 알림을 모두 취소하는 API가 있습니다.
NotificationsUtil.kt
를 엽니다.cancelAll()
를 호출하는NotificationManager
에 확장 함수를 추가합니다.
// NotificationUtils.kt
// TODO: Step 1.14 Cancel all notifications
/**
* Cancels all notifications.
*
*/
fun NotificationManager.cancelNotifications() {
cancelAll()
}
EggTimerViewModel.kt
를 열고startTimer()
함수로 이동합니다.startTimer()
내에서 시스템에서NotificationManager
인스턴스를 가져와cancelNotifications()
를 호출합니다.
// EggTimerViewModel.kt
//TODO Step 1.15 call cancel notification
val notificationManager =
ContextCompat.getSystemService(
app,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelNotifications()
- 앱을 실행하고 타이머를 시작합니다.
- 알림이 표시되면 타이머를 다시 시작하고 앱이 상태 표시줄에서 이전 알림을 자동으로 삭제하는지 확인합니다.
알림 프레임워크는 개발자가 맞춤 작업을 설정하고 필요에 따라 알림 스타일을 지정할 수 있는 다양한 맞춤설정 옵션을 제공합니다. 이 작업에서는 타이머 알림을 맞춤설정하는 방법을 알아봅니다.
1단계: 알림 스타일 지정
필요와 알림 콘텐츠에 따라 알림을 스타일링하면 알림이 눈에 띄고 애플리케이션의 확장 프로그램처럼 보입니다. 알림 프레임워크에는 도움이 되는 여러 기본 제공 스타일이 제공되며 언제든지 직접 만들 수 있습니다.
NotificationCompat
는 다음 항목에 기본 제공 스타일을 제공합니다.
BigTextStyle
: 펼쳐졌을 때 이메일 콘텐츠를 표시하는 등 큰 텍스트 블록을 표시할 수 있습니다.BigPictureStyle
: 큰 이미지 첨부파일이 포함된 대형 형식 알림을 표시합니다.InboxStyle
: 대화 스타일 텍스트 콘텐츠를 표시합니다.MediaStyle
: 미디어 재생 컨트롤을 표시합니다.MessagingStyle
: 여러 사람 간의 메시지를 포함하는 대형 형식 알림을 표시합니다.
다른 스타일에 대한 자세한 내용은 확장 가능한 알림 만들기 문서를 참고하세요. 이 단계에서는 NotificationCompat.BigPictureStyle
를 사용하여 확장 시 큰 달걀 이미지를 표시하는 확장 가능한 알림을 만듭니다.
NotificationUtils.kt
를 열고sendNotification()
함수를 찾습니다.BitmapFactory
를 사용하여resources
에서 이미지를 로드하는 것으로 시작합니다.
// NotificationUtils.kt
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.cooked_egg
)
- 새
BigPictureStyle
를 만들고 이미지를 설정합니다. - 알림이 펼쳐질 때 큰 아이콘이 사라지도록
bigLargeIcon()
을null
로 설정합니다.
// NotificationUtils.kt
// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
.bigPicture(eggImage)
.bigLargeIcon(null)
setStyle()
를bigPicStyle
로 설정하여 스타일을 지정합니다.setLargeIcon()
을 사용하여 큰 아이콘을eggImage
으로 설정합니다. 그러면 알림이 접힐 때 이미지가 더 작은 아이콘으로 표시됩니다.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
- 앱을 실행하고 타이머를 설정합니다. 알림이 처음 표시될 때는 알림 창에 축소된 상태로 표시됩니다. 알림을 펼치면 확장된 알림 영역에 큰 이미지가 표시됩니다.
2단계: 알림 작업
알림 작업은 알림에 추가할 수 있는 또 다른 맞춤설정입니다. 현재 사용자가 알림을 클릭하면 앱으로 리디렉션됩니다. 기본 알림 작업 외에도 알림에서 앱 관련 작업을 완료하는 작업 버튼을 추가할 수 있습니다.
알림에 사용자가 신속하게 응답할 수 있는 최대 세 개의 작업 버튼을 제공할 수 있습니다(예: 알림 일시중지, SMS에 답장). 이 작업 버튼은 사용자가 알림을 탭할 때 실행되는 작업과 중복되지 않아야 합니다.
작업 버튼을 추가하려면 빌더에서 PendingIntent
를 addAction()
함수에 전달하세요. 이는 setContentIntent()
를 호출하여 알림의 기본 탭 작업을 설정하는 것과 비슷합니다. 활동을 실행하는 대신 백그라운드에서 작업을 실행하는 BroadcastReceiver
를 시작하는 등 다양한 작업을 할 수 있으므로 작업을 실행해도 이미 열려 있는 앱이 중단되지 않습니다.
이 Codelab에서는 SnoozeReceiver
이라는 이름의 BoadcastReceiver
가 이미 제공되어 있습니다. SnoozeReceiver
를 사용하여 알림 작업에 대한 사용자 클릭을 수신합니다. 다음 단계에서는 사용자가 스누즈 작업 버튼을 클릭할 때 타이머 알림을 60초 동안 스누즈하는 코드를 추가합니다. 일시중지 작업을 클릭하면 SnoozeReceiver
가 인텐트를 수신하고 60초 후에 새 알림을 전송하는 새 알람을 만듭니다.
SnoozeReceiver.kt
를 엽니다. 이 클래스는 이전에 사용한AlarmReceiver
과 유사합니다. 다음 단계에서는SnoozeReceiver
의onReceive()
함수를 트리거하는 코드를 추가합니다. 간단히 말해SnoozeReceiver
의 코드는 1분 후에 새 알림을 전송하는 새 알람을 만듭니다. onReceive 함수 하단으로 스크롤하여 시스템에서 notificationManager 인스턴스를 가져오고 cancelAll을 호출합니다.
// SnoozeReceiver.kt
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelAll()
SnoozeReceiver
를 사용하려면NotificationUtils.kt
을 엽니다.-
sendNotification()
함수의 스타일 바로 뒤에SnoozeReceiver
의 새Intent
snoozeIntent
를 만듭니다. - 다음 단계의 매개변수를 예상하는
PendingIntent
에서getBroadcast()
메서드를 호출하여 대기 중인 인텐트를 만듭니다. 이PendingIntent
는 사용자가 스누즈 버튼을 탭할 때 60초 후에 새 알림을 게시하는 새 알람을 설정하는 데 시스템에서 사용됩니다. - 첫 번째 매개변수는 이
PendingIntent
가 활동을 시작해야 하는 애플리케이션 컨텍스트입니다. - 두 번째 매개변수는 요청 코드입니다. 이는 이 대기 중인 인텐트의 요청 코드입니다. 이 대기 중 인텐트를 업데이트하거나 취소해야 하는 경우 이 코드를 사용하여 대기 중 인텐트에 액세스해야 합니다.
- 다음으로 실행할 활동의 인텐트인
snoozeIntent
객체를 추가합니다. - 마지막으로 인텐트는 한 번만 사용되므로
#FLAG_ONE_SHOT
플래그 값을 추가합니다. 빠른 작업과 알림은 첫 번째 탭 후 사라지므로 인텐트는 한 번만 사용할 수 있습니다.
// NotificationUtils.kt
// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
applicationContext,
REQUEST_CODE,
snoozeIntent,
FLAGS
)
- 다음으로
notificationBuilder
에서addAction()
함수를 호출합니다. 이 함수는 사용자에게 작업을 설명하는 아이콘과 텍스트를 예상합니다.snoozeIntent
도 추가해야 합니다. 이 인텐트는 작업이 클릭될 때 올바른boadcastReceiver
를 트리거하는 데 사용됩니다.
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
R.drawable.egg_icon,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
- 알람 시계 앱을 실행하여 스누즈 작업을 테스트합니다.
- 타이머를 실행하고 앱을 백그라운드로 전환합니다. 타이머가 종료되면 알림을 펼쳐 알림에 이제 알람을 1분 더 일시중지하는 스누즈 작업 버튼이 표시되는지 확인합니다.
3단계: 알림 중요도
중요도는 알림이 사용자를 시각적 또는 청각적으로 방해하는 수준을 결정합니다. 중요도가 높은 알림은 사용자에게 더 많은 방해를 줍니다.
NotificationChannel
생성자에서 중요도 수준을 지정해야 합니다. 원래 알람 시계 앱의 중요도를 낮게 설정했습니다. IMPORTANCE_NONE(0)
에서 IMPORTANCE_HIGH(4)
까지 다섯 가지 중요도 수준 중 하나를 사용할 수 있습니다. 채널에 할당하는 중요도 수준은 채널에 게시하는 모든 알림 메시지에 적용됩니다.
채널 중요도 수준
사용자에게 표시되는 중요도 수준 | 중요도 (Android 8.0 이상) | 우선순위 (Android 7.1 이하) |
알림음이 울리며 헤드업 알림으로 표시됩니다 (화면 상단에 팝업). | ||
알림음이 울립니다. | ||
소리 안 남 | ||
알림음이 없고 상태 표시줄에 표시되지 않습니다. |
적절한 우선순위 수준을 선택하는 방법에 관한 내용은 알림 디자인 가이드의 '우선순위 수준'을 참고하세요. 앱의 알림에 대한 중요도 수준을 선택할 때는 주의해야 합니다. 채널 중요도는 사용자의 시간과 관심을 고려하여 선택해야 합니다. 중요하지 않은 알림이 긴급한 알림으로 위장되면 불필요한 경보가 발생하고 주의를 산만하게 할 수 있습니다. 사용자는 알림의 중요도 수준을 완전히 제어할 수 있으므로 성가신 알림을 만들면 사용자가 알림 채널을 완전히 사용 중지할 수 있습니다.
1.6단계에서 알림을 처음 만들 때 알림으로 사용자를 방해하지 않도록 설계되었으므로 달걀 타이머가 우선순위가 낮은 알림을 보내도록 설정되었습니다. 하지만 달걀이 너무 익기 전에 사용자의 관심을 끄는 것이 좋습니다. 알림의 중요도 수준을 변경하려면 채널 설정부터 시작하세요. 채널 중요도는 채널에 게시된 모든 알림의 중단 수준에 영향을 미치며 NotificationChannel
생성자에서 지정해야 합니다.
- 앱의 알림 채널 중요도 수준을 변경하려면
EggTimerFragment.kt
을 열고createChannel()
로 이동하세요. 중요도 수준을IMPORTANCE_LOW
에서IMPORTANCE_HIGH
로 변경합니다.
// EggTimerFragment.kt
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_HIGH
)
Android 7.1 (API 수준 25) 이하를 실행하는 기기를 지원하려면 NotificationCompat
클래스의 우선순위 상수를 사용하여 알림마다 setPriority()
도 호출해야 합니다.
NotificationUtils.kt
을 열고 알림 빌더 객체에 다음을 추가합니다.
// NotificationUtils.kt
.addAction(
R.drawable.common_google_signin_btn_icon_dark,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
// TODO: Step 2.5 set priority
.setPriority(NotificationCompat.PRIORITY_HIGH)
- 앱을 실행하기 전에 기기 또는 에뮬레이터에서 앱 아이콘을 길게 클릭하고 제거를 선택하여 이전 채널 설정을 삭제합니다. 앱을 제거하지 않으면 채널 우선순위 설정이 변경되지 않으며 알림이 게시될 때 동작이 변경되지 않습니다.
- 이제 앱을 다시 실행하고 타이머를 시작합니다. 이번에는 알림이 전송되면 앱이 포그라운드에서 실행 중인지 백그라운드에서 실행 중인지와 관계없이 화면 상단에 팝업이 표시됩니다.
4단계: 알림 배지
알림 배지는 앱에 활성 알림이 있을 때 연결된 앱의 런처 아이콘에 표시되는 작은 점입니다. 사용자는 앱 아이콘을 길게 눌러 알림을 표시할 수 있습니다.
배지라고 하는 이러한 점은 기본적으로 표시되며 앱에서 처리할 작업은 없습니다. 하지만, 배지가 알림에 적합하지 않은 경우도 있으므로 NotificationChannel
객체에서 setShowBadge(false)
를 호출하여 채널별로 배지를 사용 중지할 수 있습니다. 달걀 타이머는 특정 시점에 활성 알림이 하나만 있으므로 앱 아이콘의 배지는 사용자에게 큰 이점을 제공하지 않습니다. 다음 단계에서는 배지를 사용 중지하고 타이머 알림만 표시합니다.
- 배지를 사용 중지하려면 타이머의 채널 생성 코드에
setShowBadge(false)
를 추가합니다.
// EggTimerFragment.kt
).apply {
// TODO: Step 2.6 disable badges for this channel
setShowBadge(false)
}
- 앱을 다시 실행하고 타이머를 시작한 다음 앱 아이콘을 확인합니다. 앱 아이콘에 배지가 표시되지 않습니다.
솔루션 코드는 다운로드한 코드의 master 브랜치에 있습니다.
- NotificationManager 클래스를 사용하여 알림을 만들고, 전송하고, 업데이트하고, 취소합니다.
- createNotificationChannel 메서드와 함께 NotificationChannel 객체를 사용하여 알림 채널을 설정합니다.
- addAction()을 사용하여 알림에 빠른 작업을 추가합니다.
- setShowBadge()를 사용하여 배지를 사용 설정하거나 사용 중지합니다.
- Notification.Style에서 확장되는 스타일을 사용하여 알림 스타일을 지정합니다.
- NotificationChannel.setImportance()로 중요도 수준을 설정합니다.
Udacity 과정:
Android 개발자 문서:
이 과정의 다른 Codelab 링크는 Kotlin 기반 Android 고급 Codelab 방문 페이지를 참고하세요.