Upgrade to Pro — share decks privately, control downloads, hide ads and more …

開発者が知っておきたい通知の歴史

 開発者が知っておきたい通知の歴史

DroidKaigi 2018 Day2 18:30~19:00

Keisuke Kobayashi

February 09, 2018
Tweet

More Decks by Keisuke Kobayashi

Other Decks in Programming

Transcript

  1. ։ൃऀ͕஌͓͖͍ͬͯͨ
    ௨஌ͷྺ࢙
    Keisuke Kobayashi (kobakei)
    DroidKaigi 2018 Day2 Room1 18:30~19:00

    View Slide

  2. About Me
    • Keisuke Kobayashi
    • GitHub, Qiita: kobakei
    • Twitter: kobakei122
    • Kyash, Inc.

    View Slide

  3. Kyash

    View Slide

  4. ࠓ೔࿩͢͜ͱ
    • ௨஌ͷྺ࢙ͱޙํޓ׵ੑΛߟྀ࣮ͨ͠૷ʹ

    ͍ͭͯ঺հ͠·͢
    • ॳ৺ऀ޲͚
    • ࠷ۙAndroid։ൃΛ࢝Ίͨํ
    • Firebaseʹ೚͖ͤͬΓͰ࣮૷͍ͯ͠Δํ

    View Slide

  5. ࠓ೔࿩͞ͳ͍͜ͱ
    • αϙʔτϥΠϒϥϦ͕αϙʔτ͍ͯ͠ͳ͍

    ICSΑΓੲͷ࿩͸͠·ͤΜ
    • Wearͷ࿩͸͠·ͤΜ

    View Slide

  6. ௨஌ͷྺ࢙

    View Slide

  7. ௨஌
    • Ϣʔβʔʹͱͬͯॏཁͳػೳ
    • සൟʹมߋ͕ೖΔ
    • SDKόʔδϣϯͷ෼ذ͕ඞཁ
    • ಉ࣮͡૷Ͱ΋ɺݟ͑ํ͕OS͝ͱʹҧ͏
    • ಈ࡞֬ೝ͕େม

    View Slide

  8. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ
    *$4
    *$4
    +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ
    +FMMZ#FBO
    +FMMZ#FBO
    ,JU,BU
    -PMMJQPQ
    ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ

    ϔουΞοϓɺελΠϧ௥Ճ
    -PMMJQPQ
    .BTINBMMPX
    /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ
    /PVHBU
    0SFP νϟϯωϧɺόοδ
    0SFP

    View Slide

  9. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ
    *$4
    *$4
    +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ
    +FMMZ#FBO
    +FMMZ#FBO
    ,JU,BU
    -PMMJQPQ
    ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ

    ϔουΞοϓɺελΠϧ௥Ճ
    -PMMJQPQ
    .BTINBMMPX
    /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ
    /PVHBU
    0SFP νϟϯωϧɺόοδ
    0SFP

    View Slide

  10. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ
    *$4
    *$4
    +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ
    +FMMZ#FBO
    +FMMZ#FBO
    ,JU,BU
    -PMMJQPQ
    ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ

    ϔουΞοϓɺελΠϧ௥Ճ
    -PMMJQPQ
    .BTINBMMPX
    /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ
    /PVHBU
    0SFP νϟϯωϧɺόοδ
    0SFP

    View Slide

  11. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ
    *$4
    *$4
    +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ
    +FMMZ#FBO
    +FMMZ#FBO
    ,JU,BU
    -PMMJQPQ
    ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ

    ϔουΞοϓɺελΠϧ௥Ճ
    -PMMJQPQ
    .BTINBMMPX
    /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ
    /PVHBU
    0SFP νϟϯωϧɺόοδ
    0SFP

    View Slide

  12. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ
    *$4
    *$4
    +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ
    +FMMZ#FBO
    +FMMZ#FBO
    ,JU,BU
    -PMMJQPQ
    ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ

    ϔουΞοϓɺελΠϧ௥Ճ
    -PMMJQPQ
    .BTINBMMPX
    /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ
    /PVHBU
    0SFP νϟϯωϧɺόοδ
    0SFP

    View Slide

  13. جຊతͳ࣮૷

    View Slide

  14. ඞਢ
    • title
    • text
    • small icon
    • id

    View Slide

  15. Small icon
    • എܠಁա & നృΓ
    • ΨΠυϥΠϯ௨Γʹ࡞͍ͬͯΕ͹໰୊ͳ͍
    • https://developer.android.com/guide/practices/
    ui_guidelines/icon_design_status_bar.html
    • ϥϯνϟʔΞΠίϯΛͦͷ··࢖͍ͬͯΔ৔߹ɺ
    API 21Ҏ߱Ͱ͸നృΓʹͳΔͷͰ஫ҙ

    View Slide

  16. γϯϓϧͳ࣮૷
    val notification = NotificationCompat.Builder(context, CH_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setSmallIcon(R.drawable.ic_notification)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  17. ΄΅ඞਢ
    • color
    • defaults
    • pending intent
    • auto cancel
    • ticker (※Android 4.4·ͰͰ࢖༻)

    View Slide

  18. ݱ࣮తͳ࣮૷
    val intent = Intent(context, MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(context, 0, intent,
    PendingIntent.FLAG_UPDATE_CURRENT)
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setTicker("This is ticker") // for legacy Android
    .setSmallIcon(R.drawable.ic_notification)
    .setColor(ContextCompat.getColor(context, R.color.colorPrimary))
    .setDefaults(Notification.DEFAULT_ALL)
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  19. Large icon

    View Slide

  20. Large icon
    • ը૾μ΢ϯϩʔυΛؚΉ௨஌Λදࣔ͢Δͱ͖͸
    Serviceਪ঑
    • BroadcastReceiver͸10ඵλΠϜΞ΢τ͕

    ͋ΔͷͰɺ͕͔͔࣌ؒΔॲཧʹ͸޲͔ͳ͍
    • ݸਓతʹ͸FirebaseJobDispatcherͳͲඇಉ
    ࣌δϣϒΛ࢖͏ͱָ

    View Slide

  21. VIBRATE_PERMISSION
    • Android 4.1·Ͱ͸ඞਢ
    • ๨ΕΔͱSecurityException͕ൃੜ
    • Android 4.2Ҏ߱͸ෆཁ

    View Slide

  22. ϚςϦΞϧσβΠϯ
    • API 21͔Β
    • ௨஌ͷσβΠϯ΋େ෯ʹมΘͬͨ

    View Slide

  23. Android 4.1

    View Slide

  24. Android 8.1

    View Slide

  25. Action
    (API 16~)

    View Slide

  26. Action
    • ௨஌ʹ෇͘Ϙλϯ
    • ෳ਺ઃఆՄೳ
    • ΞΫγϣϯ͝ͱʹIntentΛઃఆ

    View Slide

  27. Actionͷ஫ҙࣄ߲
    • ICSͰ͸࢖͑ͳ͍ʢAPI 16Ҏ߱ͷΈʣ
    • ΞΠίϯ͸๨Εͣʹઃఆ͠Α͏
    • API 16~19: Χϥʔදࣔ
    • API 21~23: άϨʔදࣔ
    • API 24~: ΞΠίϯදࣔͳ͠
    • SVG࢖͑ͳ͍ͷͰ஫ҙ

    View Slide

  28. Action (API 16~19)

    View Slide

  29. Action (API 21~23)

    View Slide

  30. Action (API 24~)

    View Slide

  31. Action
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    ...
    .setContentIntent(pendingIntent)
    .addAction(R.drawable.ic_action_done, "Done", donePendingIntent)
    .addAction(R.drawable.ic_action_close, "Close", closePendingIntent)
    .setAutoCancel(true)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  32. Direct Reply
    • API 24Ҏ߱
    • ௨஌Ϗϡʔ্ͰΞϓϦىಈͤͣʹฦ৴͢Δػ

    • LINE, SlackͳͲ

    View Slide

  33. View Slide

  34. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }

    View Slide

  35. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }
    API levelͰ෼ذ

    View Slide

  36. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }
    RemoteInput࡞੒

    View Slide

  37. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }
    Reply࣌ͷIntent

    View Slide

  38. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }
    Action࡞੒

    View Slide

  39. Direct Reply
    val action = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val remoteInput = RemoteInput.Builder(KEY_REMOTE_INPUT)
    .setLabel("Reply Label")
    .build()
    val replyIntent = Intent(context, MyBroadcastReceiver::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(context, 1001,
    replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    replyPendingIntent)
    .addRemoteInput(remoteInput)
    .build()
    } else {
    NotificationCompat.Action.Builder(R.drawable.ic_action_reply, "Reply",
    pendingIntent)
    .build()
    }
    API 23ҎԼ༻ͷී௨ͷAction

    View Slide

  40. Direct Reply
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    ...
    .addAction(action)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  41. Style
    (API 16~)

    View Slide

  42. Style
    • ௨஌Ϗϡʔͷݟͨ໨Λม͑Δػೳ
    • styles.xmlͱ͸ؔ܎ͳ͍
    • API 16Ҏ߱

    View Slide

  43. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle

    View Slide

  44. ΊͬͪΌଟ͍

    View Slide

  45. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle
    ࠷සग़ɻೋߦҎ্ͷςΩετͰ࢖༻

    View Slide

  46. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle
    ը૾ܥΞϓϦͰ࢖༻

    View Slide

  47. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle
    ԻָɾಈըܥΞϓϦͰ࢖༻
    com.android.support-media-compat

    View Slide

  48. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle
    ௨஌ϏϡʔΛΧελϚΠζ

    ͢Δ࣌ʹ࢖༻ʢ͋ͱͰઆ໌ʣ

    View Slide

  49. Style
    • API 16~
    • BigTextStyle
    • BigPictureStyle
    • InboxStyle
    • API 21~
    • MediaStyle
    • API 24~
    • DecoratedCustomViewStyle
    • DecoratedMediaCustomViewStyle
    • MessagingStyle
    ϝοηʔδܥΞϓϦͰ࢖༻
    ʢޙͰઆ໌͠·͢ʣ

    View Slide

  50. Big Text Style࣮૷
    val style = NotificationCompat.BigTextStyle()
    .setBigContentTitle("Big content title")
    .setSummaryText("Big text summary")
    .bigText("This is long text. This is long text. This is long text.
    This is long text. This is long text.")
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setTicker("This is ticker") // for legacy Android
    .setStyle(style)
    ...
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  51. Big Text Style࣮૷
    val style = NotificationCompat.BigTextStyle()
    .setBigContentTitle("Big content title")
    .setSummaryText("Big text summary")
    .bigText("This is long text. This is long text. This is long text.
    This is long text. This is long text.")
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setTicker("This is ticker") // for legacy Android
    .setStyle(style)
    ...
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)
    StyleͷΠϯελϯε࡞੒

    View Slide

  52. Big Text Style࣮૷
    val style = NotificationCompat.BigTextStyle()
    .setBigContentTitle("Big content title")
    .setSummaryText("Big text summary")
    .bigText("This is long text. This is long text. This is long text.
    This is long text. This is long text.")
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setTicker("This is ticker") // for legacy Android
    .setStyle(style)
    ...
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  53. View Slide

  54. BigTextStyleͷ

    ɾBigContentTitle
    ɾBigText

    View Slide

  55. Notification.Builderͷ
    Title, Text

    View Slide

  56. ௕จͩͱ..Ͱলུ͞ΕΔ

    View Slide

  57. ௨஌ͷΧελϚΠζ

    View Slide

  58. ΧελϜϨΠΞ΢τ
    • Notification.Builder#setContent
    • API 24Ҏ߱
    • DecoratedCustomViewStyle,
    DecoratedMediaCustomViewStyle

    View Slide

  59. Notification.Builder#setContent

    View Slide

  60. val customView = RemoteViews(context.packageName, R.layout.custom_layout)
    val notificationBuilder = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setContent(customView)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notificationBuilder.build())
    Notification.Builder#setContent

    View Slide

  61. val customView = RemoteViews(context.packageName, R.layout.custom_layout)
    val notificationBuilder = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setContent(customView)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notificationBuilder.build())
    Notification.Builder#setContent
    RemoteViews࡞੒

    View Slide

  62. val customView = RemoteViews(context.packageName, R.layout.custom_layout)
    val notificationBuilder = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setContent(customView)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notificationBuilder.build())
    Notification.Builder#setContent

    View Slide

  63. RemoteViews
    • ଞϓϩηεͷϏϡʔͷ֊૚ߏ଄Λද͢Ϋϥε
    • ViewͷαϒΫϥεͰ͸ͳ͍
    • findViewByIdͰchild viewʹΞΫηεͰ͖ͳ
    ͍
    • RemoteViews#setString(viewId, methodName, value)

    ͳͲͰૢ࡞͢Δ

    View Slide

  64. DecoratedCustomViewStyle
    • API 24Ҏ߱
    • RemoteViewsΛɺΞΠίϯɺλΠτϧͳͲ

    ඪ४ͷཁૉͰϥοϓ͢ΔελΠϧ
    • ޙํޓ׵ੑ͋Γ
    • NotificationCompat.DecoratedCustomViewStyle

    View Slide

  65. DecoratedCustomViewStyle

    View Slide

  66. DecoratedCustomViewStyle

    (API 15)

    View Slide

  67. DecoratedCustomViewStyle
    val customView = RemoteViews(context.packageName, R.layout.custom_layout)
    val notificationBuilder = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setCustomContentView(customView)
    .setStyle(NotificationCompat.DecoratedCustomViewStyle())
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  68. DecoratedCustomViewStyle
    val customView = RemoteViews(context.packageName, R.layout.custom_layout)
    val notificationBuilder = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setCustomContentView(customView)
    .setStyle(NotificationCompat.DecoratedCustomViewStyle())
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  69. Colorized
    • ਐߦதͷॏཁͳ௨஌ʹͷΈɺഎܠ৭Λ෇͚Δ͜ͱ͕Ͱ͖Δ
    • ௨࿩தɺΧʔφϏͳͲ
    • Foreground ServiceͱηοτͰ࢖͏
    • ςΩετΧϥʔ͸എܠ৭͔ΒࣗಈͰܾ·ΔʢઃఆෆՄʣ
    • BLACK or WHITE
    • ίϯτϥετΛߟྀͨ͠എܠ৭Λઃఆ͢Δ͜ͱ
    • API 26Ҏ߱

    View Slide

  70. Colorized

    View Slide

  71. Colorized
    fun createColorizedNotification(service: Service): Notification {
    val context = service.applicationContext
    val intent = Intent(context, MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(context, 0, intent,
    PendingIntent.FLAG_UPDATE_CURRENT)
    return NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    ...
    .setColor(Color.DKGRAY)
    .setColorized(true)
    .setPriority(Notification.PRIORITY_HIGH)
    .build()
    }
    Colorized༗ޮԽ

    View Slide

  72. Colorized: Service
    class SampleForegroundService : IntentService("sample") {
    override fun onHandleIntent(intent: Intent?) {
    startForeground(1, createColorizedNotification(this))
    // do something...
    }
    }
    ForegroundϞʔυʹೖͬͯ

    ௨஌Λදࣔ

    View Slide

  73. ௨஌νϟϯωϧ
    (API 26~)

    View Slide

  74. ௨஌νϟϯωϧ
    • API 26Ҏ߱
    • Ϣʔβʔ͕௨஌OFFɺԻɾόΠϒઃఆͳͲΛ੾
    Γସ͑ΒΕΔ
    • targetSdkVersion=26Ҏ্Ͱ͸ରԠඞਢ
    • ࣮૷͸ࣗ෼ͰAPI levelͰ෼ذΛॻ͘

    View Slide

  75. View Slide

  76. ௨஌νϟϯωϧͷ࡞੒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel1 = NotificationChannel(CHANNEL_ID_NORMAL, "ී௨ͷνϟϯωϧ",
    NotificationManager.IMPORTANCE_DEFAULT)
    val channel2 = NotificationChannel(CHANNEL_ID_IMPORTANT, "ॏཁͳνϟϯω
    ϧ", NotificationManager.IMPORTANCE_HIGH)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.createNotificationChannel(channel1)
    nm.createNotificationChannel(channel2)
    }

    View Slide

  77. ௨஌νϟϯωϧͷ࡞੒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel1 = NotificationChannel(CHANNEL_ID_NORMAL, "ී௨ͷνϟϯωϧ",
    NotificationManager.IMPORTANCE_DEFAULT)
    val channel2 = NotificationChannel(CHANNEL_ID_IMPORTANT, "ॏཁͳνϟϯω
    ϧ", NotificationManager.IMPORTANCE_HIGH)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.createNotificationChannel(channel1)
    nm.createNotificationChannel(channel2)
    }
    API 26Ҏ߱ͷΈ࣮ߦ

    View Slide

  78. ௨஌νϟϯωϧͷ࡞੒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel1 = NotificationChannel(CHANNEL_ID_NORMAL, "ී௨ͷνϟϯωϧ",
    NotificationManager.IMPORTANCE_DEFAULT)
    val channel2 = NotificationChannel(CHANNEL_ID_IMPORTANT, "ॏཁͳνϟϯω
    ϧ", NotificationManager.IMPORTANCE_HIGH)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.createNotificationChannel(channel1)
    nm.createNotificationChannel(channel2)
    }

    View Slide

  79. ௨஌νϟϯωϧͷ࡞੒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel1 = NotificationChannel(CHANNEL_ID_NORMAL, "ී௨ͷνϟϯωϧ",
    NotificationManager.IMPORTANCE_DEFAULT)
    val channel2 = NotificationChannel(CHANNEL_ID_IMPORTANT, "ॏཁͳνϟϯω
    ϧ", NotificationManager.IMPORTANCE_HIGH)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.createNotificationChannel(channel1)
    nm.createNotificationChannel(channel2)
    }
    IDॱͰιʔτ͞ΕΔ

    View Slide

  80. ௨஌νϟϯωϧͷ࡞੒
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel1 = NotificationChannel(CHANNEL_ID_NORMAL, "ී௨ͷνϟϯωϧ",
    NotificationManager.IMPORTANCE_DEFAULT)
    val channel2 = NotificationChannel(CHANNEL_ID_IMPORTANT, "ॏཁͳνϟϯω
    ϧ", NotificationManager.IMPORTANCE_HIGH)
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.createNotificationChannel(channel1)
    nm.createNotificationChannel(channel2)
    }

    View Slide

  81. ௨஌νϟϯωϧͷࢦఆ
    val notification = NotificationCompat.Builder(context, CH_ID_NORMAL)
    .setContentTitle("This is title")
    .setContentText("This is message")
    .setSmallIcon(R.drawable.ic_notification)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)
    νϟϯωϧIDΛࢦఆ

    View Slide

  82. ϝλσʔλ

    View Slide

  83. ϝλσʔλ
    • ௨஌ʹඥͮ͘ϝλσʔλɻ௨஌ͷදࣔॱংʹӨڹ
    ͢Δ
    • setPriority
    • setCategory
    • addPerson
    • API 26͔Β͸௨஌νϟϯωϧ͕࢖ΘΕΔ

    View Slide

  84. Priority vs Importance
    • Priority…௨஌ͷ༏ઌ౓
    • Importance…νϟϯωϧͷॏཁ౓
    • ։ൃऀ͸ॳظ஋Λڭ͑Δ͚ͩͰɺ͋ͱͰϢʔ
    βʔ͕มߋՄೳ
    • ޓ׵ੑͷͨΊʹ྆ํઃఆ͠·͠ΐ͏

    View Slide

  85. ϔουΞοϓ௨஌

    (API 21~)

    View Slide

  86. ϔουΞοϓ
    • API 21Ҏ߱
    • ΞϓϦʹ͔Ϳͤͯදࣔ͞ΕΔɺ༏ઌ౓ͷߴ͍௨஌
    • දࣔϩδοΫ͕όʔδϣϯ͝ͱʹҧ͏
    • API 21 ~ 25
    • ௨஌ͷpriority
    • API 26~
    • ௨஌νϟϯωϧͷઃఆ

    View Slide

  87. ϔουΞοϓ

    View Slide

  88. ϔουΞοϓ࣮૷
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_IMPORTANT)
    ...
    .setPriority(Notification.PRIORITY_HIGH)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)
    API 26Ҏ߱
    API 25ҎԼ

    View Slide

  89. ϩοΫը໘௨஌
    (API 21~)

    View Slide

  90. ϩοΫը໘௨஌
    • API 21Ҏ߱
    • ϩοΫը໘ʹ௨஌Λදࣔ
    • ϓϥΠϕʔτͳ಺༰ΛӅ͢͜ͱ΋Մೳ
    • ୺຤ͷ௨஌ઃఆͷઃఆ࣍ୈ

    View Slide

  91. View Slide

  92. View Slide

  93. View Slide

  94. PublicʢϩοΫը໘༻ʣ
    val publicNotification = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    .setContentTitle("This is public title")
    .setContentText("This is public message")
    ...
    .build()

    View Slide

  95. PrivateʢඇϩοΫ࣌༻ʣ
    val notification = NotificationCompat.Builder(context, CHANNEL_ID_NORMAL)
    .setContentTitle("This is private title")
    .setContentText("This is private message")
    ...
    .setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
    .setPublicVersion(publicNotification)
    .build()
    val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    nm.notify(1, notification)

    View Slide

  96. όϯυϧ௨஌

    View Slide

  97. όϯυϧ௨஌
    • εϚϗ͸API 24Ҏ߱
    • ෳ਺ͷ௨஌Λ·ͱΊͯදࣔ͢Δػೳ
    • ϝʔϧɺϝοηϯδϟʔɺχϡʔεΞϓϦͰ
    සग़

    View Slide

  98. όϯυϧ௨஌

    View Slide

  99. όϯυϧ௨஌ͷ᠘
    • WearͰAPI௥Ճ͞ΕͨͷͰɺεϚϗʗλϒ
    ϨοτͰ࢖͑Δͷ͸24͔Β
    • ࣮૷ʹ͸APIϨϕϧͷ෼ذඞཁ

    View Slide

  100. αϚϦʔ
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val summaryNotification = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setGroupSummary(true)
    .setGroup(GROUP_KEY)
    .setAutoCancel(true)
    .build()
    nm.notify(1, summaryNotification)
    }

    View Slide

  101. αϚϦʔ
    API 24Ҏ߱ͰͷΈදࣔ
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val summaryNotification = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setGroupSummary(true)
    .setGroup(GROUP_KEY)
    .setAutoCancel(true)
    .build()
    nm.notify(1, summaryNotification)
    }

    View Slide

  102. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val summaryNotification = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    ...
    .setGroupSummary(true)
    .setGroup(GROUP_KEY)
    .setAutoCancel(true)
    .build()
    nm.notify(1, summaryNotification)
    }
    αϚϦʔ

    View Slide

  103. ݸผͷ௨஌
    // ֤௨஌͸લόʔδϣϯڞ௨Ͱग़͢
    val notification1 = NotificationCompat.Builder(context,
    CHANNEL_ID_NORMAL)
    .setGroup(GROUP_KEY)
    .setContentTitle("This is title 1")
    .setContentText("This is message 1")
    ...
    .build()
    val notification2 = ...
    nm.notify(2, notification1)
    nm.notify(2, notification2)

    View Slide

  104. ϝοηʔδͬΆ͍Style
    • Inbox style (API 16~)
    • Messaging style (API 24~)
    • όϯυϧ௨஌ (API 24~)
    • ݁ہͲΕΛ࢖͑͹͍͍ͷʁ

    View Slide

  105. Inbox Style

    View Slide

  106. Messaging Style

    View Slide

  107. όϯυϧ௨஌

    View Slide

  108. ࢖͍෼͚ʢࢲݟʣ
    • ݪଇόϯυϧ௨஌Λ࢖͏
    • API 23ҎԼͰ͸ɺΧελϜϏϡʔΛ࣮૷͢Δ
    • Ͱ΋໘౗ͳΒී௨ͷ௨஌Ͱ΋OK?

    View Slide

  109. View Slide

  110. ௨஌Λ࣮૷͢Δ্Ͱͷ

    ϙΠϯτ

    View Slide

  111. View Slide

  112. υΩϡϝϯτ
    • ඞͣӳޠ൛ΛݟΔ͜ͱ
    • ޓ׵ੑ·Ͱ͸ॻ͍ͯ͘Εͯͳ͍ͷͰɺͪΌΜ
    ͱओཁͳόʔδϣϯͰಈ࡞֬ೝ͠·͠ΐ͏

    View Slide

  113. ಈ࡞֬ೝ
    • αϙʔτͷ্ݶͱԼݶ͸ඞͣ֬ೝ
    • ϚςϦΞϧσβΠϯ͔ͭNougatલ΋஫ҙ
    • ୺຤ϝʔΧʔʹΑͬͯ͸ΧελϚΠζ͞Ε͍ͯ
    ΔͷͰɺNexus/Pixelਪ঑
    • ྫ: Huawei

    View Slide

  114. View Slide

  115. View Slide

  116. ·ͱΊ

    View Slide

  117. ·ͱΊ
    • ௨஌APIͷྺ࢙ΛৼΓฦΓɺ֤ػೳΛޓ׵ੑΛ
    ߟྀ࣮ͯ͠૷͢Δࡍͷ஫ҙ఺Λ঺հͨ͠
    • ͪΌΜͱυΩϡϝϯτͱಡΜͰಈ࡞֬ೝ͠ɺ
    Ϣʔβʔʹͱͬͯշదͳ௨஌Λ࣮૷͠Α͏

    View Slide

  118. αϯϓϧίʔυ
    • ࠓ೔ͷ࣮૷͸GitHubʹ্͛·ͨ͠
    • https://github.com/kobakei/
    AndroidNotificationShowcase

    View Slide

  119. and one more thing…

    View Slide

  120. ͳΔ΄ͲɺAndroid 4ܥ

    αϙʔτ͍ͯ͘͠ͷਏ͍ͧ

    View Slide

  121. Kyashͷbuild.gradle

    View Slide

  122. View Slide

  123. View Slide

  124. We are hiring!!!

    View Slide

  125. Thanks!

    View Slide