通話アプリを作成していて、近接センサ (Proximity Sensor) の近くに物体がある時 (電話中にスマホを耳に当てることによって頭が近くにある時など) に画面を消灯させたいという要望があったので、それについて調べてみましたが、案外情報が見つからず苦戦したのでメモします。
検証端末は Android 8.0.0 のみで、アプリの minSdkVersion は 21 を前提としています。
TL;DR
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "TAG")
wakeLock.acquire(DURATION_TIMEOUT)
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
という Wake lock level を指定して aqquire
すると、その Wake Lock を握っている間は近接センサが物体を検出すると画面がオフになり、物体がなくなれば画面がオンになるという挙動になります。
通話中にこの挙動を行いたい場合、 DURATION_TIMEOUT にどれくらいの値を指定するかは考えなければなりません。
あまりにも長いと Wake Lock のリリースを忘れていた場合にバッテリーを無駄に消費することになりますし、かといって短ければいいというものでもありません。
数分程度指定しておき、その時間よりも短い時間でタイマーをセットして、その時まだ通話中であれば再度 Wake Lock を取得するという方法がよさそうです。
画面を破棄するタイミングで Wake Lock をリリースするのも忘れてはいけません。
試したけどだめだった方法
Window の LayoutParams を使用する方法
参考: https://stackoverflow.com/q/9561320
window.attributes = window.attributes.apply {
flags = flags or Window.LayoutParams.FLAG_KEEP_SCREEN_ON
screenBrightness = 0
}
画面が最低限度まで暗くなるだけで、画面はオフにならずまたタッチも無効になりませんでした。
WakeLock を一瞬取得してすぐに解放する方法
参考: https://stackoverflow.com/a/6757206
val manager = getSystemService(Context.POWER_SERVICE) as PowerManager
val wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG")
wakeLock.acquire()
wakeLock.release()
何も起きませんでした。
できた方法
PROXIMITY_SCREEN_OFF_WAKE_LOCK で Wake Lock を取得する
API レベル 21 から使用できる PROXIMITY_SCREEN_OFF_WAKE_LOCK
という Wake lock level を使用して Wake Lock を取得すると、近接センサが物体を検出している間だけ画面をオフにするという望んでいた挙動を実現できました。
名前もそのままですね。
参照: https://developer.android.com/reference/android/os/PowerManager.html#PROXIMITY_SCREEN_OFF_WAKE_LOCK
A proximity wake lock does not prevent the device from falling asleep unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake locks are held, then the device will fall asleep (and lock) as usual. However, the device will not fall asleep while the screen has been turned off by the proximity sensor because it effectively counts as ongoing user activity.
リファレンスによると、この Wake Lock は FULL_WAKE_LOCK
や SCREEN_BRIGHT_WAKE_LOCK
や SCREEN_DIM_WAKE_LOCK
とは異なり、デバイスがスリープ状態になることを 妨げません 。つまりこの Wake Lock を取得していてもデバイスはスリープ状態に入ります。
しかし、後半にあるように、近接センサが物体を検出して画面がオフになっているときは、端末はスリープ状態には なりません 。これは近接センサが物体を検出しているということ = ユーザが現在端末をアクティブに使用していると判断できるためです。