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. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ   *$4   *$4

      +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ   +FMMZ#FBO   +FMMZ#FBO   ,JU,BU   -PMMJQPQ ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ
 ϔουΞοϓɺελΠϧ௥Ճ   -PMMJQPQ   .BTINBMMPX   /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ   /PVHBU   0SFP νϟϯωϧɺόοδ   0SFP
  2. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ   *$4   *$4

      +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ   +FMMZ#FBO   +FMMZ#FBO   ,JU,BU   -PMMJQPQ ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ
 ϔουΞοϓɺελΠϧ௥Ճ   -PMMJQPQ   .BTINBMMPX   /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ   /PVHBU   0SFP νϟϯωϧɺόοδ   0SFP
  3. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ   *$4   *$4

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

      +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ   +FMMZ#FBO   +FMMZ#FBO   ,JU,BU   -PMMJQPQ ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ
 ϔουΞοϓɺελΠϧ௥Ճ   -PMMJQPQ   .BTINBMMPX   /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ   /PVHBU   0SFP νϟϯωϧɺόοδ   0SFP
  5. "1*MFWFM όʔδϣϯ໊ ίʔυωʔϜ ओͳػೳ   *$4   *$4

      +FMMZ#FBO ΞΫγϣϯɺελΠϧ௥Ճ   +FMMZ#FBO   +FMMZ#FBO   ,JU,BU   -PMMJQPQ ϚςϦΞϧσβΠϯɺϩοΫεΫϦʔϯɺ
 ϔουΞοϓɺελΠϧ௥Ճ   -PMMJQPQ   .BTINBMMPX   /PVHBU όϯυϧɺϦϓϥΠɺελΠϧ௥Ճ   /PVHBU   0SFP νϟϯωϧɺόοδ   0SFP
  6. Small icon • എܠಁա & നృΓ • ΨΠυϥΠϯ௨Γʹ࡞͍ͬͯΕ͹໰୊ͳ͍ • https://developer.android.com/guide/practices/

    ui_guidelines/icon_design_status_bar.html • ϥϯνϟʔΞΠίϯΛͦͷ··࢖͍ͬͯΔ৔߹ɺ API 21Ҏ߱Ͱ͸നృΓʹͳΔͷͰ஫ҙ
  7. γϯϓϧͳ࣮૷ 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)
  8. ΄΅ඞਢ • color • defaults • pending intent • auto

    cancel • ticker (※Android 4.4·ͰͰ࢖༻)
  9. ݱ࣮తͳ࣮૷ 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)
  10. Actionͷ஫ҙࣄ߲ • ICSͰ͸࢖͑ͳ͍ʢAPI 16Ҏ߱ͷΈʣ • ΞΠίϯ͸๨Εͣʹઃఆ͠Α͏ • API 16~19: Χϥʔදࣔ

    • API 21~23: άϨʔදࣔ • API 24~: ΞΠίϯදࣔͳ͠ • SVG࢖͑ͳ͍ͷͰ஫ҙ
  11. 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)
  12. 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() }
  13. 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Ͱ෼ذ
  14. 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࡞੒
  15. 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
  16. 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࡞੒
  17. 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
  18. 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)
  19. Style • API 16~ • BigTextStyle • BigPictureStyle • InboxStyle

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

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

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

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

    • API 21~ • MediaStyle • API 24~ • DecoratedCustomViewStyle • DecoratedMediaCustomViewStyle • MessagingStyle ௨஌ϏϡʔΛΧελϚΠζ
 ͢Δ࣌ʹ࢖༻ʢ͋ͱͰઆ໌ʣ
  24. Style • API 16~ • BigTextStyle • BigPictureStyle • InboxStyle

    • API 21~ • MediaStyle • API 24~ • DecoratedCustomViewStyle • DecoratedMediaCustomViewStyle • MessagingStyle ϝοηʔδܥΞϓϦͰ࢖༻ ʢޙͰઆ໌͠·͢ʣ
  25. 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)
  26. 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ͷΠϯελϯε࡞੒
  27. 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)
  28. 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
  29. 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࡞੒
  30. 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
  31. 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)
  32. 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)
  33. 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༗ޮԽ
  34. Colorized: Service class SampleForegroundService : IntentService("sample") { override fun onHandleIntent(intent:

    Intent?) { startForeground(1, createColorizedNotification(this)) // do something... } } ForegroundϞʔυʹೖͬͯ
 ௨஌Λදࣔ
  35. ௨஌νϟϯωϧͷ࡞੒ 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) }
  36. ௨஌νϟϯωϧͷ࡞੒ 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Ҏ߱ͷΈ࣮ߦ
  37. ௨஌νϟϯωϧͷ࡞੒ 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) }
  38. ௨஌νϟϯωϧͷ࡞੒ 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ॱͰιʔτ͞ΕΔ
  39. ௨஌νϟϯωϧͷ࡞੒ 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) }
  40. ௨஌νϟϯωϧͷࢦఆ 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Λࢦఆ
  41. ϔουΞοϓ࣮૷ 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ҎԼ
  42. 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)
  43. αϚϦʔ 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) }
  44. αϚϦʔ 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) }
  45. 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) } αϚϦʔ
  46. ݸผͷ௨஌ // ֤௨஌͸લόʔδϣϯڞ௨Ͱग़͢ 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)
  47. ϝοηʔδͬΆ͍Style • Inbox style (API 16~) • Messaging style (API

    24~) • όϯυϧ௨஌ (API 24~) • ݁ہͲΕΛ࢖͑͹͍͍ͷʁ