FlutterでAndroid 14以降のローカル通知をサポートするには
この記事は2023年7月21日に執筆しています。
参照しているflutter_local_notifications
のバージョンは15.1.0+1、もしくは16.0.0-dev.1です。
※執筆にあたり慎重にドキュメントの調査などを行いましたが、不正確な情報を記述している恐れがあります
※Android OSやライブラリの更新により、異なる対応が必要になる恐れがあります
Android端末に対応したFlutterアプリケーションにおいて、サーバーからのPush通知ではないPush、つまりローカルのPush通知を表示するライブラリにflutter_local_notifications
があります。
今回はflutter_local_notifications v14からサポートされた、Android 13や14向けの「正確なアラームのスケジュール」について理解を深めます。
これからAndroid 14以降をターゲットにするケースで、参考になればと。
Andorid 13までの機能アップデート
Android 13よりUSE_EXACT_ALARM
(正確なアラームを使用するための新しい権限)が追加されました。
これはAndroid 12で追加されたSCHEDULE_EXACT_ALARM
権限(正確なアラームの権限)から、対応が一歩進んだものになります。
正確なアラームの権限
Android 12のガイドを読むと、次のような解説があります。
アプリによるシステム リソースの節約を促進するため、Android 12 以降をターゲットとし、正確なアラームを設定したアプリでは、システム設定の[特別なアプリアクセス]画面に表示される「アラームとリマインダー」機能へのアクセス権が必要になります。
この特別なアプリアクセスを取得するには、マニフェストで
SCHEDULE_EXACT_ALARM
権限をリクエストします。正確なアラームは、ユーザー向けの機能にのみ使用する必要があります。
書いてあることそのままになりますが、理解するべきは次の2点です。
- Android 12からは、正確なアラームには正確なアラームの権限が必要になった
- Android 12からは、正確なアラームの権限が必要な場合は、
SCHEDULE_EXACT_ALARM
権限のリクエストがAndroidManifest.xml
に必要になった
正確なアラームを使用するための新しい権限
Android 13のガイドを読むと、次のような解説があります。
Android 13 以降をターゲットとするアプリの場合、アプリに自動的に付与される USE_EXACT_ALARM 権限を使用できます。ただしアプリがこの権限を使用するには、次の条件の少なくとも 1 つを満たす必要があります。
- 目覚まし時計アプリまたはタイマーアプリである。
- イベントが近づくと通知を表示するカレンダー アプリである。
正確なアラームを設定する機能がアプリにあるものの、上記のどのケースも満たさない場合は、引き続き SCHEDULE_EXACT_ALARM 権限を宣言し、ユーザーがアプリのアクセスを拒否した場合も想定しておく必要があります。
書いてあることそのままになりますが、理解するべきは次の3点です。
- Android 13からは、
USE_EXACT_ALARM
権限が自動的に付与されることになった -
USE_EXACT_ALARM
を利用できるアプリは「目覚まし時計アプリまたはタイマーアプリ」または「イベントが近づくと通知を表示するカレンダー アプリ」のみ - (Android 13リリース時点において、)将来的にGoogle Playポリシーの更新で、上記のいずれかのケースを満たさない限り、
USE_EXACT_ALARM
権限の使用は禁止
Android 14の機能アップデート
Andorid 14には、13までの対応から一歩踏み込んだ変更が入っています。
カレンダーやアラームアプリ
USE_EXACT_ALARM
がインストール時に付与されるため、アラームの利用に制限はありません。
これらのアプリは、正確なアラームの設定が行えます。
カレンダーやアラームアプリではないアプリ
AndroidManifest.xml
におけるSCHEDULE_EXACT_ALARM
のリクエストは、デフォルトで拒否されます。
このため、いくつかの対応パターンを検討する必要が生じます。
- 正確なアラームを利用するため、ユーザーに権限をリクエストする
- 正確なアラームの利用ではなく、不正確なアラームの利用に切り替える
アラームの利用シーンに応じたパターンがドキュメントに記載されているため、アプリにおけるアラーム機能の必要性に応じて、個別に検討することをお勧めします。正確なアラームを必要としないユースケース
なお、この変更はTarget SDKが33(Andorid 13)以上であることに注意してください。2023年8月以降にリリースされるアプリは、Google Play Storeの制限でTarget SDKを33以上にする必要が生じます。
正確なアラームを利用するためには、Androidの特別な権限をリクエストするフローを利用する必要があります。
このフローを経ることで、ユーザーが許可を与えた場合には、アプリがSCHEDULE_EXACT_ALARM
権限を利用できるようになります。
細かなリクエストの処理を個別で書くこともできますが、Flutterの場合には、flutter_local_notifications
ライブラリに処理をお任せすることになります。
ここからは、ライブラリの実装を確認しながら、どのように正確なアラームと不正確なアラームをセットするか確認していきます。
flutter_local_notificationsのAndroidScheduleMode(4+1)
flutter_local_notifications
を利用して、時間を指定し、通知を表示する方法を確認します。
ライブラリのexampleの中から、実装を引っ張ってきて確認します。まず_zonedScheduleAlarmClockNotification()を確認してみましょう。
Future<void> _zonedScheduleAlarmClockNotification() async {
await flutterLocalNotificationsPlugin.zonedSchedule(
123,
'scheduled alarm clock title',
'scheduled alarm clock body',
tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
const NotificationDetails(
android: AndroidNotificationDetails(
'alarm_clock_channel', 'Alarm Clock Channel',
channelDescription: 'Alarm Clock Notification')),
androidScheduleMode: AndroidScheduleMode.alarmClock,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
}
この記事の関心はアラームの表示モードです。この処理ではandroidScheduleMode: AndroidScheduleMode.alarmClock
を深掘りしていきます。
AndroidScheduleModeには、4つ+1つのenumがあります。
4つ+1つと表現しているのは、お察しの通り、SCHEDULE_EXACT_ALARM
が許可されるか否の分岐があるためです。
alarmClock
Used to specify that the notification should be scheduled to be shown at the exact time specified AND will execute whilst device is in low-power idle mode. Requires SCHEDULE_EXACT_ALARM permission.
AndroidScheduleMode.alarmClock
はSCHEDULE_EXACT_ALARM
を前提とした動きをする選択肢です。
Android 14以降ではAndroidManifest.xml
経由で取得できなくなったため、このリクエストを送る前に権限を取得しておく必要があります。
権限が取得されていない場合には、SecurityException
がAndroidのExceptionとして発生することになります。
実装を確認する限り、try-catchで処理されていないため、Pluginの処理内でクラッシュするようです。ご注意ください。
なおflutter_local_notifications
では、v16にて「SCHEDULE_EXACT_ALARM
をリクエストする機能」の追加が予定されています。
詳細はv16のリリース時に追加されるドキュメントで確認する必要がありますが、現時点ではMethodChannelFlutterLocalNotificationsPlugin.requestExactAlarmsPermission()
を呼び出すことで、権限をリクエストする処理を(単体で)呼び出せるようになります。
Androidのコードも簡単に確認しておきましょう。
DartでalarmClock
を選択すると、JavaのScheduleMode.alarmClock
にマッピングされます。
JavaではuseAlarmClock()
のみがtrue
となり、alarmClock
専用の処理がなされます。
FlutterLocalNotificationsPlugin.java
の処理を確認すると、1回/繰り返しの通知設定共にAlarmManagerCompat.setAlarmClock
を呼び出します。
alarmClock以外
flutter_local_notifications
のv14で追加された、Android 13以降で増えたアラームの設定に対応するためのenumです。
それぞれ、名前から明らかではあるのですが、PluginのJava側で次のtrue/falseの表現を持ちます。
exact | exactAllowWhileIdle | inexact | inexactAllowWhileIdle | |
---|---|---|---|---|
useAllowWhileIdle() |
false |
true |
false |
true |
useExactAlarm() |
true |
true |
false |
false |
FlutterLocalNotificationsPlugin.java
を確認すると、useExactAlarm()
とuseAllowWhileIdle()
は次の処理がなされます。
useAllowWhileIdle()
true
の場合、AlarmManagerCompat.setAndAllowWhileIdle
またはAlarmManagerCompat.setExactAndAllowWhileIdle
が呼び出されます。
ドキュメントにある通り、低電力アイドルモード、つまりDozeの最中にアラームが発生することを許可します。
前述のalarmClock
との違いとしては、Doze の制限事項を確認する限り「alarmClock
の場合にはDozeを解除する、AllowWhileIde
の場合には解除しない」点になるようです。Dozeがバッテリー消費に寄与する機能である以上、この設定の差は端末のバッテリー消費に影響すると考えられます。
useExactAlarm()
true
の場合、ExactAlarmが利用できるかを確認し、利用できる場合にAlarmManagerCompat.setExact
若しくはAlarmManagerCompat.setExactAndAllowWhileIdle
が呼び出されます。
この時ExactAlarmが利用できるかはAlarmManagerCompat.canScheduleExactAlarms
を呼び出しチェックするだけです。(利用できない場合には、通知がキャンセルされ、Log出力がなされます。)
つまり、権限の取得については処理を行いません。
flutter_local_notifications
のv16がリリースされれば、必要な権限についても、flutter_local_notifications
ライブラリで完結できます。
しかしv15まででは、(アラームやカレンダーアプリでない場合には、)他のライブラリや自前の実装により、SCHEDULE_EXACT_ALARM
の権限を取得する必要があります。
結論
- カレンダーやアラームを主な機能としたアプリは、これまで通りでOK
- カレンダーやアラームを主な機能としていないアプリは、権限周りを調査してからリリースする必要アリ
- 正確なアラームを必要としない場合は、不正確なアラームを利用する方がユーザーと開発者双方にとって嬉しい
-
flutter_local_notifications
ライブラリは神
Discussion