Androidのカスタムプッシュ通知でリッチなUXをユーザーに届けよう

こんにちは。Pairs JPのAndroidエンジニアの栗村貴尚(@t-kurimura)です。

eureka Native Advent Calendar 2017の11日目は、昨日の@muukiiさんの「もし、アプリではデータを永続化しなかったら?」に続いて、Androidのプッシュ通知についてお伝えします。

Androidは、プッシュ通知に独自のボタンを複数つけられたり、大きな画像などをつけられたり、アプリ独自のUIを表示できたりします。iOSでは、最近OS10以上では画像に加えて、動画、音声も表示できるようになったと知りました。Androidも、2012年ごろにリリースされた4系からは、同じようにわざわざアプリを開かなくとも情報を得られるような画像を表示できたり、独自のボタンをつけることでアプリを通知から起動する前にどのような操作(閲覧する・保存する)をするかなどを選ぶことができます。

かなり昔からある技術ですが、最近になりようやく色々なサービスで見るようになってきた印象なので、改めてまとめてみました。

対象となる読者の方

  • プッシュ通知の実装経験がなく、これから実装してみようと思うAndroidエンジニアの方
  • カスタム通知の実装をしようと思っているが、面倒そうで避けていたAndroidエンジニアの方
  • 普段、iOSを使っているので、AndroidのPush通知のUIをあまり見たことがないデザイナーの方
  • Androidのプッシュ開封率をあげたいマーケティング担当の方
  • その他、Androidアプリで実現可能なプッシュ通知のUXを簡単に知っておきたい方

というわけで、Androidのプッシュ通知で結構色々なことができるのですが、あまり知られていない気がしているので、エンジニアに限らず色々な方々に知っていただけたらと思います。

プッシュ通知受信時の挙動

さて、ここから実際にAndroidに表示されるプッシュ通知の話です。今回の実装例はandroid.support.v4のNotificationCompatを利用しています。

受信時のバイブレーションとサウンド

Androidのプッシュ通知は、端末にバイブレーションさせるか、プッシュ受信時の音を鳴らすかどうかを通知ごとに指定できることができます。もちろん各ユーザーが、Android自体の設定で「バイブレーション鳴らさない」といったものや「重要な通知のみ」と指定していたり、マナーモードに設定されていたりするとそちらが優先されます。特に何も指定されていないとユーザーが気づきにくいプッシュ通知になっている可能性があります。

        NotificationCompat.Builder(this, "sample")
              // ...省略
             .setDefaults(Notification.DEFAULT_SOUND or Notification.DEFAULT_VIBRATE) // <-- ここで指定
             .build()

受信時のUIの種類

通知を受信した場合の表示は、一般的に大きく2種類に分けられると思います。

Normalバージョン

受信時はステータスバー(画面の一番上の通知されたアイコンや電波状況、電池残量、時刻などが並んでいる部分)に、通知のアイコンが表示されます。ノーティフィケーションドロワー(画面上からしたにスワイプして引き出す通知一覧画面)を開くと通知が見れるようになります。

HeadUpバージョン

HeadUpバージョンはホーム画面や他のアプリを見ていても画面最上部に通知の内容が表示されます。この用途はMaterialDesignGuidelineの中では、もっとも重要な通知のみなので、テキストメッセージ・アラーム・着信などに限るように指示されています。

        NotificationCompat.Builder(this, "sample")
            // ...省略
            .setDefaults(Notification.DEFAULT_VIBRATE)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .build()

画面最上部に通知を表示させるためには上の二つの値を設定します。Defaultsの方は、Notification.DEFAULT_SOUND でも問題ないですが、setPriorityの方だけでは画面上部に表示されないので注意してください。

プッシュ通知にボタンを表示する

ボタンをつけることでユーザーは「プッシュ通知を開く」という動作だけではなく、そのほかの動作も選ぶことができます。以下の例は、「一覧を見る」と「記事を見る」の両方の動作をユーザーが選ぶことができます。

        val articlePageIntent = TaskStackBuilder.create(this)
            .addNextIntent(ArticleDetailPageActivity.createIntnt(this))
            .getPendingIntent(1, PendingIntent.FLAG_ONE_SHOT)

        val articleListPageIntent = TaskStackBuilder.create(this)
            .addNextIntent(ArticleListPageActivity.createIntnt(this))
            .getPendingIntent(1, PendingIntent.FLAG_ONE_SHOT)

        NotificationCompat.Builder(this, "sample")
              // ...省略
            .addAction(0, "記事を見る", articlePageIntent)
            .addAction(0, "一覧を見る", articleListPageIntent)
            .build()

各ボタンにアイコンを表示できたり、ボタンを3つ表示できたりもします。4つ以上addActionしても4つ目以降は反映されません。

アイコンと文字の両方を入れることももちろん可能ですが、3つの場合ボタンの場合は、サイズが狭く文字が2文字程度しか表示できなそうなので、避けた方が良さそうです。

プッシュ通知に画像を表示する

        NotificationCompat.Builder(this, "sample")
            .setAutoCancel(true)
            .setSmallIcon(R.drawable.eureka_square)
            .setStyle(NotificationCompat.BigPictureStyle().bigPicture(ogImageBitmap))
            .setLargeIcon(profileBitmap)
            .setContentTitle("Eureka Tech Blog")
            .setContentText("これはAdventCalendar 7日目の記事です")
            .setContentIntent(articlePageIntent)
            .build()

プッシュ通知がノーティフィケーションドロワーにひとつしかないときはNotificationCompat.BigPictureStyleで設定したBitmapの画像が大きく表示されます。それ以外の場合は通知をしたに引っ張るようにフリックすると画像が表示されます。

setLargeIconもNotificationCompat.BigPictureStyleもBitmapから指定する必要があるので、ペイロードなどで受け取ったURLを使う場合はライブラリを使用したりして、Bitmapを取得する必要があります。

拡張レイアウトを利用する

カスタム通知レイアウトを使うと画像やテキストとボタンといった様々なViewの配置を自由に組むことができます。

自由にと言っても完全に独自UIを組めるわけではなく、「最大の縦幅は256dpまで」、「RemoteViewを使って実装するため、カスタムビューなどは使えず、特定のクラスしか使えない」と言った制約はあります。
一方で、ボタンなどはいくつでも配置できるようになるので、通知の中のどのボタンを押したかによって処理を分けてあげることができます。(とはいえ、ユーザーは通知の詳細な内容を見ずに意思決定するので、ここに置くボタンは熟慮が必要そうです)

        val profileBitmap = (resources.getDrawable(R.drawable.kurimura) as BitmapDrawable).bitmap

        val remoteView = RemoteViews(packageName, R.layout.custom_notification).apply {
          setTextViewText(R.id.title, "RxJava,Kotlin,Databindingでイケてる入力フォームをスッキリ実装する")
          setTextViewText(R.id.date_etc, "2017.12.07 Tech by Takahisa Kurimura")
          setTextViewText(R.id.snipet, "はじめまして。Pairs事業部の栗村貴尚(@t-kurimura)です。\n主に、Pairs JapanのAndroid版の機能開発をしたり、スクラムマスターとして...")

          setImageViewBitmap(R.id.profile_photo, profileBitmap)

          setOnClickPendingIntent(R.id.share_btn_1, share1PagePendingIntent)
          setOnClickPendingIntent(R.id.share_btn_2, share2PagePendingIntent)
          setOnClickPendingIntent(R.id.share_btn_3, share3PagePendingIntent)
        }

        NotificationCompat.Builder(this, "sample")
            .setSmallIcon(R.drawable.eureka_square)
            .setDefaults(Notification.DEFAULT_SOUND or Notification.DEFAULT_VIBRATE)
            .setPriority(Integer.MAX_VALUE)
            .setCustomBigContentView(remoteView)
            .setCustomHeadsUpContentView(remoteView)
            .setContentIntent(articlePageIntent)
            .build()
            .let { notificationManager.notify(0, it) }

R.layout.custom_notification は、普通のページと変わらず、以下の要素で実装しています。

以下、実際にこのプシュ通知を受け取った時のイメージをデモにしています。BigContentViewを使ったときでも、ノーティフィケーションドロワーで通知が小さくなると、ContentText`, `ContentTitleなどが表示されるため、入れておいた方が安心です。

終わりに

プッシュ通知のカスタムレイアウトは昔からある技術ですが、あまり見かけることは多くありませんでした。最近でこそニュースアプリなどで見かけるようになりましたが、見た目も目を惹きやすく、うまく使えば利便性も高いのでユーザーにより早く情報を届け、早くアクションできるようにするという価値を届ける良い手段のひとつだと思います。

Androidにこのようなプッシュ通知の方法があることが知れたり、思い出せたりする良い機会となれば幸いです。

Advent Calendar明日は、海藤さんさんの記事です!お楽しみに!

  • このエントリーをはてなブックマークに追加

エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!

Recommend

SEOに強いメディアサイトになるためにデザインでできる3つのこと ファーストビュー編

Go言語でもくもくする会「ごもく会」第二回を開催しました