はじめに
この記事はAndroid Advent Calendar 2022 24日目の記事です。
やりたかったことはAndroidで強制的に音を鳴らすことだったのですが、ミュート解除周りでハマったのでその時の話を書きます。
コードからミュートを解除する
adjustStreamVolumeを使う
コードからミュートを解除するにはAudioManagerのメソッドであるadjustStreamVolume
を使うとできます。このメソッドは音量を調節するためのメソッドですが、ミュートとアンミュートを指定することができます。
第一引数には使用したいストリームを指定します。今回はアラーム音のためのストリームであるSTREAM_ALARM
を使用します。第2引数には音量を上げるか下げるかを指定することができます。この第2引数にADJUST_UNMUTE
を指定すると、ミュートを解除することができます。
第3引数は flag です。今回は0で大丈夫です。
この辺りの定数に関してはこちらをご覧ください。
val audioManager = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
try {
audioManager.adjustStreamVolume(
AudioManager.STREAM_ALARM,
AudioManager.ADJUST_UNMUTE,
0
)
// 音量を5に設定
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, 5, 0)
} catch (e: Exception) {
Timber.e("ミュートの解除に失敗しました。", e)
}
しかし、実はこれだと端末によっては通知オフ状態(サイレントモード)は解除できません。バイブレーションモードのみ解除することができます。
ringerModeを変更する
別の方法として、ringerMode
を変更する方法があります。
AudioManager
のringerMode
をRINGER_MODE_NORMAL
に変更します。
val audioManager = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
try {
audioManager.ringerMode = AudioManager.RINGER_MODE_NORMAL
// 音量を5に設定
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, 5, 0)
} catch (e: Exception) {
Timber.e("ミュートの解除に失敗しました。", e)
}
コードからサイレントモードを設定する
ミュートを解除して必要な効果音等を鳴らした後、もう一度サイレントモードやバイブレーションモードに戻したいですよね。
サイレントモードやバイブレーションモードに戻すにはまずAndroidManifestにACCESS_NOTIFICATION_POLICY
のパーミッションを追加します。
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
その上でringerMode
を元に戻します。そのために解除する時に元の状態を記憶しておく必要があります。
val audioManager = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
try {
// 元の状態を記憶
isVibration = audioManager.ringerMode == AudioManager.RINGER_MODE_VIBRATE
isSilent = audioManager.ringerMode == AudioManager.RINGER_MODE_SILENT
// サイレントモードを解除
audioManager.ringerMode = AudioManager.RINGER_MODE_NORMAL
// 音量を5に設定
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, 5, 0)
} catch (e: Exception) {
Timber.e("ミュートの解除に失敗しました。", e)
}
// 元の状態に戻す
if (audioManager.ringerMode == AudioManager.RINGER_MODE_NORMAL) {
// バイブレーションに戻す
if (isVibration) {
audioManager.ringerMode = AudioManager.RINGER_MODE_VIBRATE
} else if (isSilent) {
// サイレントモードに戻す
audioManager.ringerMode = AudioManager.RINGER_MODE_SILENT
}
}
しかし、ここで問題があります。
もう一度サイレントモードを解除…できない?
コードからサイレントモードに設定した後、もう一度サイレントモードを解除することができなくなります。
これはAndroidのサイレントモードの仕様が原因です。
Androidのサイレントモードの仕様
Androidには、音量から変更できるいわゆる通知オフの状態と、設定画面などから設定することができる強いサイレントモードの2種類があります。
コードからサイレントモードの設定をすると、この強いサイレントモードに移行してしまいます。
そして、この強いサイレントモードは、端末によってあったりなかったりします。設定の方法もバラバラです。
社内のいくつかの検証端末で確認した結果、以下のような結果になりました。
機種名 | OSバージョン | 強いサイレントモードの有無 | 設定場所 |
---|---|---|---|
Pixcel 6 Pro | 11 | あり | コントロールセンター |
Xperia 10 Ⅱ / Ⅲ | 11 | あり | 設定画面 |
OPPO A55s 5G | 11 | あり | 設定画面 |
Galaxy Z Flip3 5G | 12 | なし | |
HUAWEI nova lite 3 | 9 | なし | |
AQUOS R2 compact | 9 | なし |
そして、強いサイレントモードに移行してしまうとコードからは解除できなくなってしまいます。
私が確認した中で強いサイレントモードにしか移行できない端末は以下の2つでした。この端末は通知オフを解除/設定することもできませんでした。
- Pixcel 6 Pro
- Xperia 10 Ⅱ / Ⅲ
通知オフの状態を解除/設定できる端末は以下の端末でした。
- OPPO A55s 5G
- Galaxy Z Flip3 5G
この2つに関しては、adjustStreamVolumeを使う方法で通知オフ状態の解除ができました。しかしGalaxyに関しては、通知オフ/バイブレーション状態でもringerModeがNORMALから変更されませんでした。その状態でrigerModeを変更しようとすると、アプリがクラッシュしてしまいました。
以下の端末は通知オフの状態の解除/設定はできませんでした。
- HUAWEI nova lite 3
- AQUOS R2 compact
まとめ
コードをからミュートを解除する方法はadjustStreamVolumeを使う方法とringerModeを変更する方法があります。しかし、サイレントモードには機種によって強いサイレントモードに移行してしまうものがあり、元の状態に戻せなくなってしまうことがありました。
結局私はadjustStreamVolumeを使うを採用し、音が鳴らない機種があることに関しては認めて貰うことになりました。。アプリ側から強いサイレントモードを設定してしまうのも、ミュート状態を元に戻さないのも理想的ではないので…
この辺り知見ある方がいらっしゃいましたら教えてくださるとありがたいです。