화면 상단에 배터리, 네트워크, 시간 등이 표시된 부분을 상태바라고 한다. 이 상태바는 시스템이 제어하는 부분이기 때문에 앱이 제어할 수 없다. 그래서 상태바에 알림을 출력하려면 시스템에 의뢰를 해야하는데, 이때 알림을 제공하는 API 를 사용한다. 알림은 NotificationManager의 notify() 함수로 발생한다.
Notification을 만드려면 NotificationCompat.Builder가 필요하다.
Builder(context: Context!)
Builder(context: Context!, channelld: String!)
API 레벨 26에서 채널이라는 개념이 추가되었다. 앱의 알림을 채널로 구분할 수 있으며, 사용자가 환경설정에서 어떤 앱의 알림을 받을지 말지 설정할 수 있다.
API 레벨 26 이전에서는 사용자가 알림을 안받겠다고 하면 해당 앱의 알림을 모두 안받지만, API 레벨 26 이후에서는 앱의 특정 채널의 알림만 받지 않겠다고 설정할 수 있다.
NotificationChannel(id: String!, name: CharSequence!, importance: Int)
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_HIGH
)
// 채널에 다양한 정보설정
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)
// 채널을 이용해 빌더 생성
builder = NotificationCompat.Builder(this, channelId)
}else {
builder = NotificationCompat.Builder (this)
}
빌더를 만들었으면 이 빌더를 이용해 Notification 객체를 만들어야 한다. Notification 객체에는 스몰 아이콘과 발생 시각, 제목, 내용 등의 정보가 담겨있다.
빌더의 setter 함수를 이용해 설정한다.
builder.setSmallIcon(android.R.drawable.ic_notification_overlay)
builder.setwhen (System.currentTimeMillis())
builder.setContentTitle("Content Title")
builder.setContentText("Content Massage")
NotificationManager 클래스의 notify() 함수로 알림을 띄운다
manger.notify(11, builder.build())
builder.build() 함수가 Notification 객체를 만들어 알림이 발생한다. 첫번째 매개변수는 알림의 식별값으로 개발자가 마음대로 지정해서 적으면 된다.
매개변수로 알림의 식별값을 넘겨준다.
manager.cancel(11)
사용자가 알림을 터치나 스와이프해도 알림이 사라지지 않게 하려면 builder 의 setter 함수로 지정해야 한다.
// 알림을 터치하면 이벤트는 발생하지만 알림이 사라지지 않는다.
builder.setAutoCancel(false)
// 알림을 스와이프해도 알림이 사라지지 않는다.
builder.setOngoing(true)
사용자가 알림을 터치했을 때 앱의 액티비티 화면을 실행하도록 하려면 터치 이벤트를 구현해야 한다.상태바는 시스템이 제어하는 영역이므로, onTouchEvent() 함수로는 처리할 수 없다.
따라서 사용자가 알림을 터치했을 때 실행해야 하는 정보를 Notification 객체에 넣어두고, 실제 이벤트가 발생하면 Notification 객체에 등록된 이벤트 처리 내용을 시스템이 실행하는 구조이다.
사용자가 알림을 터치하면 인텐트(Intent)를 이용해 앱의 액티비티 또는 브로드캐스트 리시버를 실행한다.
Intent를 준비한 후 Notification 객체에 담아서 이벤트가 발생할 때 Intent를 실행해달라고 시스템에게 의뢰해야 한다. 이때 PendingIntent 클래스를 이용하는데, PendingIntent 클래스는 컴포넌트별로 실행을 의뢰하는 함수를 제공한다.
static fun getActivity(context: Context!, requestCode: Int, intent: Intent!, flags: Int): PendingIntent!
static fun getBroadcast (context: Context!, requestCode: Int, intent: Intent!, flags: Int): PendingIntent!
static fun getService(context: Context!, requestCode: Int, intent: Intent, flags: Int): PendingIntent!
val intent = Intent(this, DetailActivity::class.java)
val pendingIntent =
PendingIntent.getActivity(this, 10, intent, PendingIntent.FLAG_IMMUTABLE)
builder.setContentIntent(pendingIntent) //터치 이벤트 등록
알림은 최대 3개의 액션(알람 취소, 전화 앱의 수신이나 거부, ...)을 처리할 수 있다.
터치 이벤트와 마찬가지로 사용자가 액션을 터치할 때 실행할 인텐트 정보를 PendingIntent로 구성해서 등록해야 한다.
매개변수로 액션의 정보를 담고있는 Action 객체를 전달한다.
open fun addAction(action: Notification.Action!): Notification.Builder
Action 객체는 Action.Builder로 만든다.
Builder(icon: Int, title: CharSequence!, intent: PendingIntent!)
val actionIntent = Intent (this, OneReceiver::class.java)
val actionPendingIntent = PendingIntent.getBroadcast(this, 20, actionIntent,
PendingIntent.FLAG IMMUTABLE)
builder.addAction(
NotificationCompat.Action.Builder(
android.R.drawable.stat_notify_more,
"Action",
actionPendingIntent
).build( )
)
원격 입력이란 액션의 한 종류로, 알림에서 사용자의 입력을 직접 받는 것이다.
val KEY_TEXT_REPLY = "key_text_reply"
var replyLabel: String = "답장"
var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run{
setLabel (replyLabel)
build()
}
RemoteInput 은 API 레벨 20에서 추가했다. 따라서 minSDK 가 더 낮으면 호환성을 고려해야 하는데 android.app.RemoteInput이 아닌 androidx.core.app.RemoteInput 이용하면 따로 호환성 처리를 해주지 않아도 된다.
val replyIntent = Intent(this, ReplyReceiver::class.java)
val replyPendingIntent = PendingIntent.getBroadcast(this, 30, replyIntent,
PendingIntent.FLAG_MUTABLE)
RemoteInput은 알림에서 사용자의 입력을 받는 것이므로 앱의 화면이 출력되게 하지 않고 브로드캐스트 리시버를 실행해 백그라운드에서 사용자 입력을 처리하는 게 일반적이다.
builder.addAction(
NotificationCompat.Action.Builder(
R.drawable.send,
"답장",
replyPendingIntent
).addRemoteInput(remoteInput).build()
)
아래 코드에서 getCharSequence 의 매개변수로 전달하는 문자열이 RemoteInput을 만들 때 지정한 식별값과 같아야 한다는 것에 주의해야 한다.
val replyTxt = RemoteInput.getResultsFromIntent(intent)
?.getCharSequence("key_text_reply")
브로드캐스트 리시버에서 사용자의 입력 글을 받은 후 알림을 갱신해줘야 한다. 이때 알림 객체의 식별값을 사용해 갱신해주면 된다.
아래 코드는 알림에 글을 잘 받았다는 신호를 보내는 것으로, 이렇게 보내면 알림에서 글을 입력하는 부분이 사라지게 된다.
manager.notify(11, builder.build())
앱에서 서버로 파일을 올리는 등의 어떤 작업이 진행되는데 시간이 걸릴 때 진행상황을 알려줄 수 있다.
빌더에 setProgress() 함수만 추가해주면 된다.
open fun setProgress(max: Int, progress: Int, indeterminate: Boolean): Notification.Builder
아래는 10초동안 progress 바의 진행값을 증가시키는 예시이다.
builder.setProgress(100, 0, false)
manager.notify(11, builder.build())
thread {
for (i in 1..100) {
builder.setProgress (100, i, false)
manager.notify(11, builder.build())
SystemClock.sleep(100)
}
}