こんにちは。Androidアプリエンジニアの筒井です。
11月27日の夜にヤフー社内で他社さんも含めたクローズドな合同勉強会を行いました。
その時の自分の発表内容を今日は書こうと思います。
LollipopでのNotification
5.0からのNotificationで自分が興味があるところについて調べてみました。
- Heads Up Notification
- ロック画面上での通知の表示
- その他 Tipsなど
環境
調べるにあたって利用した環境です。
- Nexus5 (OS: 5.0)
- ソースコード android-5.0.0_r7
Heads Up Notification
Lollipopから導入された新しい通知の仕組みです。Statusbar, Activityの上にかぶる感じで表示されます。
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANELなど指定されていました。
参考)com.android.systemui.statusbar.phone.PhoneStatusBar#addHeadsUpView
private void addHeadsUpView() {
int headsUpHeight = mContext.getResources()
.getDimensionPixelSize(R.dimen.heads_up_window_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, headsUpHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.gravity = Gravity.TOP;
lp.setTitle("Heads Up");
lp.packageName = mContext.getPackageName();
lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
mWindowManager.addView(mHeadsUpNotificationView, lp);
}
どうやって表示するんだろう?
まずは、Javadocを見てみよう。heads-upで検索、検索。
どうもSystem側で判断して表示するらしい。どういうロジックでHeads-Upするんだろうか?
まずはサンプルを見てみよう。
SamplesにあるLNotificationsを利用。
問題なく、Heads Up Notificationが表示されました。では、次にコードも見てみましょう。
サンプル内のコード
Notification createNotification(boolean makeHeadsUpNotification) {
…
if (makeHeadsUpNotification) {
Intent push = new Intent();
push.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
push.setClass(getActivity(), LNotificationActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(getActivity(), 0,
push, PendingIntent.FLAG_CANCEL_CURRENT);
notificationBuilder
.setContentText("Heads-Up Notification on Android L or above.")
.setFullScreenIntent(fullScreenPendingIntent, true);
}
return notificationBuilder.build();
}
なるほど、setFullScreenIntent()でHeads Upされるようだ。
※ setFullScreenIntentの第二引数をfalseにしてもHeads Up Notification表示されました。
実際のロジックはどうなんだろう。気になりますね。
よし本体のソースも見てみよう!
本体側のソースコードの探検
まずは、HeadsUpでgrep。いくつか出てきましたが、それっぽいのが見つかりました!
frameworks/base/packages/SystemUI/src/com/android/systemui/PhoneStatusBar.java
public void addNotification(StatusBarNotification notification, ...) {
if (mUseHeadsUp && shouldInterrupt(notification)) {
...
if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
// 1. Populate mHeadsUpNotificationView
mHeadsUpNotificationView.showNotification(interruptionCandidate);
return;
}
...
このshouldInterrupt() がtrueを返せば、mHeadsUpNotificationView.showNotification() をコールしそうだ。
mHeadsUpNotificationViewの名前通り、これがHeads Upに関連しそうです。
もう少し見ていきましょう。
frameworks/base/packages/SystemUI/src/com/android/systemui/BaseStatusBar.java
※ PhoneStatubarの親クラスがBaseStatusBarです。
protected boolean shouldInterrupt(StatusBarNotification sbn) {
...
boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
|| (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
|| notification.sound != null
|| notification.vibrate != null;
boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
boolean isFullscreen = notification.fullScreenIntent != null;
boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
...
return interrupt;
}
fullScreenIntentの設定、もしくは、high priorityでかつ、noisy(音やバイブ)かtickerTextを持っている時にtrueを返すようだ。そして、ここでtrueを返すと、Heads Upされそう。
メモ: sbn.getScore()はpriorityの設定で決定されます。
詳しくは、frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java 内をScoringで検索すると、内部のロジックが分かります。
サンプルを修正して確認してみよう
Notification createNotification(boolean makeHeadsUpNotification) {
…
if (makeHeadsUpNotification) {
notificationBuilder
.setVibrate(new long[]{100, 0, 100, 0, 100, 0})
.setPriority(Notification.PRIORITY_HIGH)
.setContentText("Heads-Up Notification on Android L or above.");
//.setFullScreenIntent(fullScreenPendingIntent, false);
}
return notificationBuilder.build();
}
本体側のソースを見る限り、Notification.PRIORITY_HIGH と vibrate の組み合わせで Heads Upされるはず…。
表示されました:)
なんとなくわかってきた感じがする。実際のコミットログも見てみよう。
Heads Up Notificationに関するコミットログ
コミットログもいくつか出てきたのですが、わりとずばりのものが見つかりました。
一部を抜粋します。https://android.googlesource.com/platform/frameworks/base/+/47c20a1%5E!/
The heads up notification is influences by full screen, sound, vibration and priority.
うん、ソースコードのロジックとマッチしている内容ですね。
改めてドキュメントを見なおしてみる
ソースコードも見て理解が深まってきたので、改めてドキュメントを見なおしてみました…。
わりと近いことが小さくポロッと書いてました。
ただ、ここには、soundやvibrationのことは書いてないなぁ…。
そもそも、High Priorityだけだと表示されなかったので、少しドキュメントが足りていない感じがします。
ここまででわかったこと
Heads Up Notificationを表示したいときは、
- fullScreenIntentを設定
- high priority以上で音・バイブ・tickerTextのいずれかを設定
注意: tickerText は試したのですが、自分の環境ではHeads Upされませんでした。コード上ではそういうロジックだったのですが…仕様かバグかまでは時間が足りず調べていません。
ロック画面上での通知の表示
さて、Lollipopからロック画面上で通知が表示されるようになっています。
こちらも興味深いですね。
上のスクリーンショットでは、メッセージの内容も表示されています。ここは、非表示にしたい人もいそうですね。設定を見てみましょう。
通知の表示の設定
「プライベートな通知内容を非表示にする」に変更してみよう。
上の設定は、ロック画面のPINやパターンを設定したら、表示されます。
Visibilityについて
設定でも、ある程度コントロールできるが、アプリ側からは表示のコントロールできないんだろうか? という疑問が出てきますが…APIあります!
サンプルにあるので、実際に試してみましょう。とりあえず、Public,Private,Secret それぞれ送ります。
public, private, secretの3種類とロック画面の通知の表示設定の関連性
ロック画面の表示設定とvisibilityの設定の関連性を調べてみました。「すべての通知内容を表示にする」の場合は、Secretにしても表示される仕様のようです。
「すべての通知内容を表示にする」
「プライベートな通知内容を非表示にする」
SmartLockについて
LollipopからSmartLockが導入されました。設定している場合、ロック画面の認証をスキップできます。例えば、Android Wearを設定していて、そのAndroid Wearがスマホと接続しているとロック画面認証は不要になります。
設定画面は、以下の様なものです。
SmartLockの設定していた場合は、安全と見なされるので、VisibilityをSecretにしていても、ロック画面に表示されます。
この点は注意が必要です。
Android Wearへの通知について
サンプル使って、テストで通知を出していたら、Android Wearにも通知がどんどん来ました…。テストの時はよいとしても、実際のアプリ開発については、setLocalOnly(true)をして、必要なものだけWear側に通知した方が親切ですね。
まとめ
- 通知はロック画面にも表示される。
- 設定項目が複雑、設定によって動作も変わる。 Secretにしていても、ユーザーが「すべての通知内容を表示する」にしていたらロック画面にも表示されたりとか。
- Heads Upしたいときは、fullScreenIntentを設定(もしくは、High Priority以上にしてバイブ・通知音の設定)
関連情報
参考ページなど
まだ、あまり日本語情報が少ないので、オフィシャルの情報が参考になります。
- YoutubeのDevBytes https://www.youtube.com/watch?v=Uiq2kZ2JHVY&noredirect=1
- オフィシャルのNotificationのページ http://developer.android.com/design/patterns/notifications.html
参考にしたソース
- BaseStatusBar.java (com.android.systemui.statusbar)
- NotificationData.java (com.android.systemui.statusbar)
- PhoneStatusBar.java (com.android.systemui.statusbar.phone)
- NotificationManagerService.java (com.android.server.notification)
※ 引用したAndroidのフレームワーク、サンプルのコードのライセンスは、どちらも Apache 2.0 に準じます。
https://source.android.com/source/licenses.html
※ 引用したJavadocのドキュメントのライセンスは、Apache 2.0 に準じます。
http://developer.android.com/reference/android/app/Notification.html
さいごに
Lollipopで通知は大きく改善しているので、上手に使っていきたいですね。
本記事もNotificationの導入にあたって参考になればうれしく思います。
読んでくれた、みなさまありがとうございます!
-------
※ Google、Nexus、Androidは、Google Inc.の登録商標または商標です。
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました