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 full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

  5. ௨஌ͷྺ࢙

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

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

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

    View full-size slide

  12. جຊతͳ࣮૷

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  15. γϯϓϧͳ࣮૷
    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 full-size slide

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

    View full-size slide

  17. ݱ࣮తͳ࣮૷
    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 full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  21. Action
    (API 16~)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. Action (API 16~19)

    View full-size slide

  25. Action (API 21~23)

    View full-size slide

  26. Action (API 24~)

    View full-size slide

  27. 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 full-size slide

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

    • LINE, SlackͳͲ

    View full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

  32. 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 full-size slide

  33. 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 full-size 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()
    }
    API 23ҎԼ༻ͷී௨ͷAction

    View full-size slide

  35. 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 full-size slide

  36. Style
    (API 16~)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  39. ΊͬͪΌଟ͍

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

  45. 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 full-size slide

  46. 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 full-size slide

  47. 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 full-size slide

  48. BigTextStyleͷ

    ɾBigContentTitle
    ɾBigText

    View full-size slide

  49. Notification.Builderͷ
    Title, Text

    View full-size slide

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

    View full-size slide

  51. ௨஌ͷΧελϚΠζ

    View full-size slide

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

    View full-size slide

  53. Notification.Builder#setContent

    View full-size slide

  54. 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 full-size slide

  55. 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 full-size slide

  56. 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 full-size slide

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

    ͳͲͰૢ࡞͢Δ

    View full-size slide

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

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

    View full-size slide

  59. DecoratedCustomViewStyle

    View full-size slide

  60. DecoratedCustomViewStyle

    (API 15)

    View full-size slide

  61. 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 full-size slide

  62. 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 full-size slide

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

    View full-size slide

  64. 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 full-size slide

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

    ௨஌Λදࣔ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  68. ௨஌νϟϯωϧͷ࡞੒
    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 full-size slide

  69. ௨஌νϟϯωϧͷ࡞੒
    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 full-size slide

  70. ௨஌νϟϯωϧͷ࡞੒
    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 full-size slide

  71. ௨஌νϟϯωϧͷ࡞੒
    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 full-size slide

  72. ௨஌νϟϯωϧͷ࡞੒
    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 full-size slide

  73. ௨஌νϟϯωϧͷࢦఆ
    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 full-size slide

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

    View full-size slide

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

    View full-size slide

  76. ϔουΞοϓ௨஌

    (API 21~)

    View full-size slide

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

    View full-size slide

  78. ϔουΞοϓ

    View full-size slide

  79. ϔουΞοϓ࣮૷
    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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  83. 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 full-size slide

  84. όϯυϧ௨஌

    View full-size slide

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

    View full-size slide

  86. όϯυϧ௨஌

    View full-size slide

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

    View full-size slide

  88. αϚϦʔ
    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 full-size slide

  89. αϚϦʔ
    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 full-size slide

  90. 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 full-size slide

  91. ݸผͷ௨஌
    // ֤௨஌͸લόʔδϣϯڞ௨Ͱग़͢
    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 full-size slide

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

    View full-size slide

  93. Messaging Style

    View full-size slide

  94. όϯυϧ௨஌

    View full-size slide

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

    View full-size slide

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

    ϙΠϯτ

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  101. and one more thing…

    View full-size slide

  102. ͳΔ΄ͲɺAndroid 4ܥ

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

    View full-size slide

  103. Kyashͷbuild.gradle

    View full-size slide

  104. We are hiring!!!

    View full-size slide