今回は、個人開発アプリ「かってに通知メモ」に通知音の選択機能を実装した話をまとめました。
Androidの通知音まわりはかなりクセがあって、かなり苦労したのでブログに残しておきます。同じところでハマっている方の参考になれば幸いです。
きっかけ
「かってに通知メモ」は、日本語で「3分後に洗濯」「明日の朝に病院」などと入力するだけで、自動で通知をセットしてくれるアプリです。
リリース後、ユーザーから「通知音を自分で選びたい」という要望をもらいました。
確かに、通知音が選べるとより使いやすくなりますよね。ということで実装することにしました。
最初の壁:app_settingsパッケージが動かない
最初は app_settings パッケージを使って、Android の通知設定画面を開く方法を試みました。
しかしリリースビルドでは動かないという問題が発生。
MissingPluginException(No implementation found for method openSettings
on channel com.spencerccf.app_settings/methods)
原因はProGuardによるコードの最適化でした。proguard-rules.pro にルールを追加しても解決せず、最終的にはアプリ内で通知音を選択できるUIを実装する方向に切り替えました。
wav形式を選んだ理由
通知音のファイル形式はいくつか選択肢があります。
.mp3.wav.ogg
iOSとAndroidの両方で動作することを考えて、今回は .wav 形式を選びました。
ファイル名は以下のような命名規則にしました:
short_notifybell.wav (3秒)
short_transparent.wav (3秒)
short_shiningtime.wav (3秒)
medium_musicbox.wav (6秒)
medium_chime.wav (10秒)
medium_fantasy.wav (11秒)
long_crystal.wav (18秒)
long_arpeggio.wav (28秒)
short_ medium_ long_ のプレフィックスで長さが一目瞭然になるので、管理しやすくなりました。
Androidへのファイル配置
Androidで通知発火時に指定した音を鳴らすには、res/raw/ フォルダにファイルを配置する必要があります。
android/app/src/main/res/raw/short_notifybell.wav
android/app/src/main/res/raw/short_transparent.wav
android/app/src/main/res/raw/medium_musicbox.wav
...
assets/sounds/ に置くだけでは通知発火時には使えないので注意が必要です。
AndroidNotificationDetailsの設定
通知チャンネルに音声を紐付けるには、RawResourceAndroidNotificationSound を使います。
final channelId = 'katteni_channel_$soundName';
android: AndroidNotificationDetails(
channelId,
'かってに通知メモ',
channelDescription: '時間指定メモの通知',
importance: Importance.max,
priority: Priority.max,
icon: '@drawable/ic_notification',
playSound: true,
sound: RawResourceAndroidNotificationSound(soundName),
),
ポイントはチャンネルIDを音ごとに変えること。
Androidの通知チャンネルは一度作成すると音を変更できません。そのため、音の名前をチャンネルIDに含めることで、音ごとに別チャンネルを作るようにしました。
設定画面のUI実装
設定画面では、音名と再生時間を一覧表示して選択できるようにしました。
final sounds = [
{'value': 'short_notifybell', 'label': 'ノティファイベル (3秒)'},
{'value': 'short_transparent', 'label': 'トランスペアレント (3秒)'},
{'value': 'medium_musicbox', 'label': 'オルゴール (6秒)'},
// ...
];
タップ時にはプレビュー再生して、3秒後に自動停止するようにしています。
onTap: () async {
setState(() => _notificationSound = sound['value']!);
await _saveSettings();
await SoundService.playSoundByName(sound['value']!);
},
SoundService.playSoundByName() の中では、ファイルのプレフィックスで拡張子を判定しています。
static Future<void> playSoundByName(String name) async {
try {
final isWav = name.startsWith('short_') ||
name.startsWith('medium_') ||
name.startsWith('long_');
final ext = isWav ? 'wav' : 'mp3';
await _player.play(AssetSource('sounds/$name.$ext'));
await Future.delayed(const Duration(seconds: 3));
await _player.stop();
} catch (e) {
debugPrint('音声再生エラー: $e');
}
}
ハマったポイントまとめ
1. リリースビルドでないと通知音が確認できない
デバッグビルドではデフォルト音が鳴るため、必ずリリースビルドでテストする必要があります。これに気づくまでにかなり時間を使いました。
2. 通知チャンネルは一度作ると変更できない
チャンネルIDを変えずに音だけ変えようとしても反映されません。必ずチャンネルIDを音名ごとに分けましょう。
3. アプリのデータを消去しないと新しいチャンネルが反映されない
チャンネルIDを変えた場合、古いチャンネルがキャッシュされています。テスト時は設定→アプリ→データを消去してから確認しましょう。
まとめ
Androidの通知音実装で大事なポイントをまとめます。
- 音声ファイルは
res/raw/に配置する RawResourceAndroidNotificationSoundで音を指定する- チャンネルIDは音ごとに分ける
- 必ずリリースビルドで動作確認する
iOSの通知音実装については、また別の記事でまとめます!
かってに通知メモは App Store・Google Play で公開中です。ぜひ使ってみてください😊
