Posted at

Android で近接センサが物体を検出している間だけ画面をオフにする

More than 1 year has passed since last update.

通話アプリを作成していて、近接センサ (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_LOCKSCREEN_BRIGHT_WAKE_LOCKSCREEN_DIM_WAKE_LOCK とは異なり、デバイスがスリープ状態になることを 妨げません 。つまりこの Wake Lock を取得していてもデバイスはスリープ状態に入ります。

しかし、後半にあるように、近接センサが物体を検出して画面がオフになっているときは、端末はスリープ状態には なりません 。これは近接センサが物体を検出しているということ = ユーザが現在端末をアクティブに使用していると判断できるためです。