[Android ๊ธฐ์ดˆ] 14. ์•Œ๋ฆผ (Notification)
๋ฐ˜์‘ํ˜•

 

 

 

 

 

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

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

developer.android.com

 

 

1. ์•Œ๋ฆผ (Notification)

 

  • ์•ฑ์˜ UI์™€ ๋ณ„๋„๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ์•ฑ๊ณผ ๊ด€๋ จํ•œ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ
  • ์•Œ๋ฆผ์„ ํ„ฐ์น˜ํ•˜์—ฌ ํ•ด๋‹น ์•ฑ์„ ์—ด ์ˆ˜ ์žˆ๊ณ , ๋ฐ”๋กœ ๊ฐ„๋‹จํ•œ ์ž‘์—…(์˜ˆ : ๋ฌธ์ž ๋‹ตํ•˜๊ธฐ)์„ ํ•  ์ˆ˜ ์žˆ์Œ (Android 7.0๋ถ€ํ„ฐ)
  • ๋ณดํ†ต ๋‹จ๋ง๊ธฐ ์ƒ๋‹จ ๋ถ€๋ถ„์— ํ‘œ์‹œ๋˜๊ณ , ์•ฑ ์•„์ด์ฝ˜์˜ ๋ฐฐ์ง€๋กœ๋„ ํ‘œ์‹œ (Android 8.0๋ถ€ํ„ฐ)

 

 

 

2.  ์•Œ๋ฆผ ์ฑ„๋„ ๋งŒ๋“ค๊ธฐ

 

์•Œ๋ฆผ ์ฑ„๋„ ๋งŒ๋“ค๊ธฐ ๋ฐ ๊ด€๋ฆฌ  |  Views  |  Android Developers

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

developer.android.com

 

  • Android 8.0์ด์ƒ์˜ ๊ฒฝ์šฐ๋Š” ์•Œ๋ฆผ์„ ๋งŒ๋“ค๊ธฐ ์ „์— ์•Œ๋ฆผ ์ฑ„๋„์„ ๋จผ์ € ๋งŒ๋“ค์–ด์•ผํ•จ
  • ์•Œ๋ฆผ ์ฑ„๋„์€ ์•Œ๋ฆผ์„ ๊ทธ๋ฃนํ•˜์—ฌ ์•Œ๋ฆผ ํ™œ์„ฑํ™”๋‚˜ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์žˆ์Œ
  • ํ˜„์žฌ ์•ฑ์ด ์‹คํ–‰ ์ค‘์ธ ์•ˆ๋“œ๋กœ์ด๋“œ ๋ฒ„์ „์„ ํ™•์ธํ•˜์—ฌ 8.0์ด์ƒ์ธ ๊ฒฝ์šฐ๋งŒ ์ฑ„๋„ ์ƒ์„ฑ

 

private val myNotificationID = 1
private val channelID = "default"

private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0
        val channel = NotificationChannel(channelID, "default channel",
            NotificationManager.IMPORTANCE_DEFAULT)
        channel.description = "description text of this channel."
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}
  • ๊ณ ์œ ํ•œ ์ฑ„๋„ ID, ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ๋˜๋Š” ์ด๋ฆ„, ์ค‘์š”๋„ ์ˆ˜์ค€์„ ์‚ฌ์šฉํ•˜์—ฌ NotificationChannel ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑ
  • ํ•„์š”ํ•œ ๊ฒฝ์šฐ setDescription()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ์Šคํ…œ ์„ค์ •์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œ๋˜๋Š” ์„ค๋ช…์„ ์ง€์ •
  • ์•Œ๋ฆผ ์ฑ„๋„์„ createNotificationChannel()์— ์ „๋‹ฌํ•˜์—ฌ ๋“ฑ๋ก

 

 

3. ์•Œ๋ฆผ ์ƒ์„ฑ

 

์•Œ๋ฆผ ๋งŒ๋“ค๊ธฐ  |  Views  |  Android Developers

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

developer.android.com

private val myNotificationID = 1

private fun showNotification() {
    val builder = NotificationCompat.Builder(this, channelID)
        .setSmallIcon(R.mipmap.ic_launcher)
        .setContentTitle("title")
        .setContentText("notification text")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())
}
  • 1) NotificationCompat.Builder ๊ฐ์ฒด์—์„œ ์•Œ๋ฆผ์— ๋Œ€ํ•œ UI์ •๋ณด์™€ ์ž‘์—…์„ ์ง€์ •
          - setSmallIcon() : ์ž‘์€ ์•„์ด์ฝ˜
          - setContentTitle() : ์ œ๋ชฉ
          - setContentText() : ์„ธ๋ถ€ ํ…์ŠคํŠธ
  • 2) NotificationCompat.Builder.build()ํ˜ธ์ถœ
          - Notification๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
  • 3) NotificationManagerCompat.notify()๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‹œ์Šคํ…œ์— Notification๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ

 

 

 

4. ์•Œ๋ฆผ ์ค‘์š”๋„

Android 8.0 ์ด์ƒ์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ ์ฑ„๋„์˜ ์ค‘์š”๋„๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ

  • Android 7.1 (API ์ˆ˜์ค€ 25) ์ดํ•˜์—์„œ๋Š” ์•Œ๋ฆผ์˜ ์ค‘์š”๋„๊ฐ€ ์•Œ๋ฆผ์˜ priority์— ์˜ํ•ด ๊ฒฐ์ •
    NotificationCompat.Builder(this,channelID).setPriority(NotificationCompat.PRIORITY_DEFAULT)
  • Android 8.0 (API ์ˆ˜์ค€ 26) ์ด์ƒ์—์„œ ์•Œ๋ฆผ์˜ ์ค‘์š”๋„๋Š” ์•Œ๋ฆผ์ด ๊ฒŒ์‹œ๋œ ์ฑ„๋„์˜ importance์— ์˜ํ•ด ๊ฒฐ์ •
    NotificationChannel  - (channelID, "defaultchannel", NotificationManager.IMPORTANCE_DEFAULT)
  • ์‚ฌ์šฉ์ž๋Š” ์‹œ์Šคํ…œ ์„ค์ •์—์„œ ์•Œ๋ฆผ ์ฑ„๋„์˜ ์ค‘์š”๋„๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ

 

 

 

5. ์•Œ๋ฆผ ํ™•์žฅ๋ทฐ

 

์•Œ๋ฆผ ๋งŒ๋“ค๊ธฐ  |  Views  |  Android Developers

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

developer.android.com

 

5-1. ๊ธด ํ…์ŠคํŠธ

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€
	builder.setStyle(NotificationCompat.BigTextStyle() 
		.bigText("Much longer text that cannot fit one line..."))
์˜ˆ์‹œ

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

 

5-2. ์ด๋ฏธ์ง€

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€
			.setStyle(NotificationCompat.BigPictureStyle()
				.bigPicture(myBitmap))
์˜ˆ์‹œ

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.new_post)
        .setContentTitle(imageTitle)
        .setContentText(imageDescription)
        .setLargeIcon(myBitmap)
        .setStyle(NotificationCompat.BigPictureStyle()
                .bigPicture(myBitmap)
                .bigLargeIcon(null)) // ์•Œ๋ฆผ์„ ํ™•์žฅํ•  ๋•Œ ์•„์ด์ฝ˜์„ ์ˆจ๊น€
        .build()

 

5-3. ๋ฒ„ํŠผ ์ถ”๊ฐ€

  • ์•Œ๋ฆผ์— ์‚ฌ์šฉ์ž๊ฐ€ ์‹ ์†ํ•˜๊ฒŒ ์‘๋‹ตํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—… ๋ฒ„ํŠผ์„ ์ตœ๋Œ€ 3๊ฐœ๊นŒ์ง€ ์ œ๊ณต ๊ฐ€๋Šฅ
  • ์ด๋Ÿฌํ•œ ์ž‘์—… ๋ฒ„ํŠผ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ๋ฆผ์„ ํƒญํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ์ž‘์—…๊ณผ ์ค‘๋ณต๋˜์ง€ ์•Š์•„์•ผ ํ•จ
  • ์ž‘์—… ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด PendingIntent๋ฅผ addAction() ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌ
    ์ด๋Š” ํ™œ๋™์„ ์‹คํ–‰ํ•˜๋Š” ๋Œ€์‹  ์•Œ๋ฆผ์˜ ๊ธฐ๋ณธ ํƒญ ์ž‘์—…์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Œ
  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” BroadcastReceiver๋ฅผ ์‹œ์ž‘ํ•ด ์ž‘์—…์ด ์ด๋ฏธ ์—ด๋ ค ์žˆ๋Š” ์•ฑ์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๋“ฑ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์Œ
val ACTION_SNOOZE = "snooze"

val snoozeIntent = Intent(this, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(this, 0, snoozeIntent, 0)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, getString(R.string.snooze),
                snoozePendingIntent)
ํ˜น์€ intent๋กœ Activity๋ฅผ ์‹œ์ž‘ํ•จ (Snooze ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด TestActivity ์‹คํ–‰ ์˜ˆ์‹œ)

val intent = Intent(this, TestActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val builder = NotificationCompat.Builder(this, channelID)
	.setSmallIcon(R.mipmap.ic_launcher)
    .setContentTitle("Notification Title")
    .setContentText("Notification body")
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    .addAction(R.drawable.android_hsu, "Snooze", pendingIntent)
NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())

 

5-4. ํ”„๋กœ๊ทธ๋ ˆ์Šค๋ฐ” ์ถ”๊ฐ€

  • ์–ธ์ œ๋“ ์ง€ ์™„๋ฃŒ๋œ ์ž‘์—…์˜ ์–‘์„ ์ถ”์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ setProgress(max, progress, false)๋ฅผ ํ˜ธ์ถœ
  • ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” '์™„๋ฃŒ' ๊ฐ’(์˜ˆ: 100) / ๋‘ ๋ฒˆ์งธ๋Š” ์–ผ๋งˆ๋‚˜ ์™„๋ฃŒ๋˜์—ˆ๋Š”์ง€ ์ง„ํ–‰์ •๋„ / ๋งˆ์ง€๋ง‰์€ ํ™•์ •๋œ ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ์ค„
  • ์ž‘์—…์ด ์ง„ํ–‰๋จ์— ๋”ฐ๋ผ ์—…๋ฐ์ดํŠธ๋œ progress ๊ฐ’์œผ๋กœ setProgress(max, progress, false)๋ฅผ ๊ณ„์† ํ˜ธ์ถœํ•˜๊ณ  ์•Œ๋ฆผ์„ ๋‹ค์‹œ ๋ฐœํ–‰. ์ž‘์—…์ด ๋๋‚˜๋ฉด progress๋Š” max์™€ ๊ฐ™์•„์•ผ ํ•จ
  • ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ์ค„์„ ์‚ญ์ œํ•˜๋ ค๋ฉด setProgress(0, 0, false)๋ฅผ ํ˜ธ์ถœ
  • ์ฐธ๊ณ ๋กœ ํ”„๋กœ๊ทธ๋ ˆ์Šค๋ฐ”๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•ฑ์ด ์ง€์†์ ์œผ๋กœ ์•Œ๋ฆผ์„ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ด ์ฝ”๋“œ๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค์—์„œ ์‹คํ–‰๋˜๋„๋ก ํ•ด์•ผํ•จ
  • ์ฐธ๊ณ ๋กœ ๋‚˜์ค‘์— ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ์–ดํ”Œ์„ ๊ตฌ์ƒํ•œ๋‹ค๋ฉด ๋‹ค์šด๋กœ๋“œ ์ง„ํ–‰๋ฅ ์„ ์ถ”์ ํ•˜๋Š” ์ž์ฒด ์•Œ๋ฆผ์„ ์ œ๊ณตํ•˜๋Š” DownloadManager๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์Œ
val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentTitle("Picture Download")
    setContentText("Download in progress")
    setSmallIcon(R.drawable.ic_notification)
    setPriority(NotificationCompat.PRIORITY_LOW)
}
val PROGRESS_MAX = 100
val PROGRESS_CURRENT = 0
NotificationManagerCompat.from(this).apply {
    // Issue the initial notification with zero progress.
    builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false)
    notify(notificationId, builder.build())

    // Do the job that tracks the progress here.
    // Usually, this is in a worker thread.
    // To show progress, update PROGRESS_CURRENT and update the notification with:
    // builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
    // notificationManager.notify(notificationId, builder.build());

    // When done, update the notification once more to remove the progress bar.
    builder.setContentText("Download complete")
            .setProgress(0, 0, false)
    notify(notificationId, builder.build())
}

 

 

 

6. ์•Œ๋ฆผ์— ์•กํ‹ฐ๋น„ํ‹ฐ ์—ฐ๊ฒฐ

  • ์•Œ๋ฆผ์„ ํด๋ฆญํ•  ๋•Œ ์•Œ๋ฆผ์€ ์‚ฌ๋ผ์ง€๊ณ , SecondActivity๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ๊ตฌํ˜„
  • SecondActivity๊ฐ€ ์‹คํ–‰๋œ ์ƒํƒœ์—์„œ Back์ด๋‚˜ Up์„ ๋ˆ„๋ฅด๋ฉด MainActivity๊ฐ€ ๋‚˜์˜ค๋„๋ก ๊ตฌํ˜„
  • ์•Œ๋ฆผ์„ ํ„ฐ์น˜ํ•˜๋ฉด ์ผ๋ฐ˜ ์•กํ‹ฐ๋น„ํ‹ฐ์ธ SecondActivity๊ฐ€ ์‹œ์ž‘, ์ด ๋•Œ MainActivity์œ„์— SecondActivity๊ฐ€ ์žˆ๋Š” ๋ฐฑ์Šคํƒ ์ƒ์„ฑ
  • (์‚ฌ์‹ค MainAcitivity๊ฐ€ ์ด๋ฏธ ๋ฐฑ์Šคํƒ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— TaskStackBuilder๋กœ ๋ฐฑ์Šคํƒ์„ ์กฐ์ž‘ํ•˜์ง€ ์•Š์•„๋„ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘)
  • ๋ฐฑ์Šคํƒ์— ์—†๋Š” ๋‹ค๋ฅธ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ SecondActivity์˜ parentActivity๋กœ ํ•˜๋ฉด ๋‹ฌ๋ผ์ง
AndroidManifest.xml์˜ SecondActivity ์ •์˜ ๋ถ€๋ถ„

<activity android:name=".SecondAcitivty" android:parentActivityName=".MainActivity" />
PendingIntent ์ƒ์„ฑํ•˜๊ณ  ์•Œ๋ฆผ ๋“ฑ๋ก

val intent = Intent(this, SecondActivity::class.java)
val pendingIntent = with (TaskStackBuilder.create(this)) {
	addNextIntentWithParentStack(intent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}
val builder = NotificationCompat.Builder(this, channelID)
	.setSmallIcon(R.mipmap.ic_launcher)
    .setContentTitle("Notification Title")
    .setContentText("Notification body")
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true) // ์œ ์ €๊ฐ€ ํ„ฐ์ง€ํ•˜๋ฉด ์•Œ๋ฆผ ์ž๋™ ์‚ญ์ œ
NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())

 

 

 

 

 

๐Ÿ’ก Android API 33์ด์ƒ์—์„œ ๊ถŒํ•œ ์‹œ์Šคํ…œ ๋ณ€๊ฒฝ

 

์•Œ๋ฆผ ๋Ÿฐํƒ€์ž„ ๊ถŒํ•œ  |  Views  |  Android Developers

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

developer.android.com

  • ์•ˆ๋“œ๋กœ์ด๋“œ 12(API ๋ ˆ๋ฒจ 33)๋ถ€ํ„ฐ, POST_NOTIFICATIONS ๊ถŒํ•œ์„ ์•ฑ์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ ํŒŒ์ผ์— ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€ ํ•„์ˆ˜
  • ์‚ฌ์šฉ์ž์˜ ํ”„๋ผ์ด๋ฒ„์‹œ๋ฅผ ๊ฐ•ํ™”ํ•˜๊ณ , ์•ฑ์ด ์‚ฌ์šฉ์ž์˜ ๋™์˜ ์—†์ด ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ
  • ์‚ฌ์šฉ์ž๋Š” ์•ฑ ์„ค์น˜ ํ›„ ์ฒ˜์Œ ์•Œ๋ฆผ์„ ์ˆ˜์‹ ํ•  ๋•Œ ์ด ๊ถŒํ•œ์„ ๋ถ€์—ฌํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋Œ€ํ™”์ƒ์ž๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Œ 

 

AndroidManifest.xml ์— ๊ถŒํ•œ ์ถ”๊ฐ€

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <!-- API 33 ์ด์ƒ์„ ์œ„ํ•œ ์•Œ๋ฆผ ๊ถŒํ•œ ์ถ”๊ฐ€ -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    ...
</manifest>
์‚ฌ์šฉ์ž ๊ถŒํ•œ์„ ํ™•์ธํ•˜๊ณ  ๊ถŒํ•œ ์š”์ฒญ

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
        // ์•Œ๋ฆผ ๊ถŒํ•œ์ด ์—†๋‹ค๋ฉด, ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ถŒํ•œ ์š”์ฒญ
        val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
            putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
        }
        startActivity(intent)
    }
}

 

 

๐Ÿ’ก ์•Œ๋ฆผ ๊ตฌํ˜„ ์˜ˆ์‹œ

 

๋”๋ณด๊ธฐ
activity_main.xml


<?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">

    <Button
        android:id="@+id/notificationButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="์•Œ๋ฆผ ๋ณด๋‚ด๊ธฐ"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

MainActivity.kt


class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        binding.notificationButton.setOnClickListener{
            notification()
        }
    }

    fun notification(){
        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

        val builder: NotificationCompat.Builder
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            // 26 ๋ฒ„์ „ ์ด์ƒ
            val channelId="one-channel"
            val channelName="My Channel One"
            val channel = NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_DEFAULT
            ).apply {
                // ์ฑ„๋„์— ๋‹ค์–‘ํ•œ ์ •๋ณด ์„ค์ •
                description = "My Channel One Description"
                setShowBadge(true)
                val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
                val audioAttributes = AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ALARM)
                    .build()
                setSound(uri, audioAttributes)
                enableVibration(true)
            }
            // ์ฑ„๋„์„ NotificationManager์— ๋“ฑ๋ก
            manager.createNotificationChannel(channel)

            // ์ฑ„๋„์„ ์ด์šฉํ•˜์—ฌ builder ์ƒ์„ฑ
            builder = NotificationCompat.Builder(this, channelId)

        }else {
            // 26 ๋ฒ„์ „ ์ดํ•˜
            builder = NotificationCompat.Builder(this)
        }

				val bitmap = BitmapFactory.decodeResource(resources, R.drawable.flower)
        val intent = Intent(this, SecondActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
        // ์•Œ๋ฆผ์˜ ๊ธฐ๋ณธ ์ •๋ณด
        builder.run {
            setSmallIcon(R.mipmap.ic_launcher)
            setWhen(System.currentTimeMillis())
            setContentTitle("์ƒˆ๋กœ์šด ์•Œ๋ฆผ์ž…๋‹ˆ๋‹ค.")
            setContentText("์•Œ๋ฆผ์ด ์ž˜ ๋ณด์ด์‹œ๋‚˜์š”.")
            setStyle(NotificationCompat.BigTextStyle()
                .bigText("์ด๊ฒƒ์€ ๊ธดํ…์ŠคํŠธ ์ƒ˜ํ”Œ์ž…๋‹ˆ๋‹ค. ์•„์ฃผ ๊ธด ํ…์ŠคํŠธ๋ฅผ ์“ธ๋•Œ๋Š” ์—ฌ๊ธฐ๋‹ค ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ๊ธดํ…์ŠคํŠธ ์ƒ˜ํ”Œ์ž…๋‹ˆ๋‹ค. 
์•„์ฃผ ๊ธด ํ…์ŠคํŠธ๋ฅผ ์“ธ๋•Œ๋Š” ์—ฌ๊ธฐ๋‹ค ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.์ด๊ฒƒ์€ ๊ธดํ…์ŠคํŠธ ์ƒ˜ํ”Œ์ž…๋‹ˆ๋‹ค. ์•„์ฃผ ๊ธด ํ…์ŠคํŠธ๋ฅผ ์“ธ๋•Œ๋Š” ์—ฌ๊ธฐ๋‹ค ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค."))
            setLargeIcon(bitmap)
//            setStyle(NotificationCompat.BigPictureStyle()
//                    .bigPicture(bitmap)
//                    .bigLargeIcon(null))  // hide largeIcon while expanding
            addAction(R.mipmap.ic_launcher, "Action", pendingIntent)
        }


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

}
๋ฐ˜์‘ํ˜•
 ๐Ÿ’ฌ C O M M E N T