Letting the Device Sleep, Intelligently

Android makes it easy for activities to keep the screen on while the activity is in the foreground, by means of android:keepScreenOn and setKeepScreenOn().

However, these are very blunt instruments, and too many developers simply ask to keep the screen on constantly, even when that is not needed and cause cause excessive battery drain.

For example, let’s talk about Eufloria HD.

Eufloria is a delightful game. It arranges to keep the screen on during game play. That’s probably a good idea, as screen timeouts can be fairly short (e.g., 15 seconds), and while usually the user will be tapping or swiping something within that period of time, they might not always do so while still playing the game.

However, Eufloria always keeps the screen on. So, if you press the in-game pause button, they keep the screen on while the game is paused. This might lead the user to press pause, put down their tablet (expecting it to fall asleep in a normal period of time), and then have the tablet keep going and going and going… until the battery runs dead.

Whether you use setKeepScreenOn() or directly use a WakeLock (as the permissions suggest that Eufloria does), it is useful to think of three tiers of user interaction.

The first tier is when your app is doing its “one big thing”: playing the game, playing the video, displaying the digital book, etc. If you expect that there will be periods of time when the user is actively engaged with your app, but is not interacting with the screen, keep the screen on.

The second tier is when your app is delivering something to the user that probably would get used without interaction in the short term, but not indefinitely. For example, Eufloria might reasonably expect that 15 seconds could be too short to have the screen time out, but if the user has not done anything in 5-10 minutes, most likely they are not in front of the game. Similarly, a digital book reader should not try to keep the screen on for an hour without user interaction.

:: note to self: fix this in my digital book reader… ::

The third tier is when your app is doing anything other than the main content, where normal device behavior should resume. A video player might keep the screen on while the video is playing, but if the video ends, normal behavior should resume. After all, if the person who had been watching the video fell asleep, they will not be in position to press a power button.

The first and third tiers are fairly easy from a programming standpoint. Just acquire() and release() the WakeLock, or toggle setKeepScreenOn() between true and false.

The second tier – where you are willing to have a screen timeout, just not too quickly – requires you to add a bit more smarts to your app. A simple, low-overhead way of addressing this is to have a postDelayed() loop, to get a Runnable control every 5-10 seconds. Each time the user interacts with your app, update a lastInteraction timestamp. The Runnable compares lastInteraction with the current time, and if it exceeds some threshold, release the WakeLock or call setKeepScreenOn(false). When the user interacts again, though, you will need to re-acquire the WakeLock or call setKeepScreenOn(true). Basically, you have your own inactivity timing mechanism to control when you are inhibiting normal inactivity behavior or not.

Now, in Eufloria’s defense, they might be using a third-party game framework that does not provide this degree of control at the present time. That, though, means that the game framework could use a bit of sophistication in this area.

By having a more intelligent use of WakeLock and setKeepScreenOn(), you can deliver value to the user while not accidentally causing excessive battery drain. Users do not always remember to press the power button, so you need to make sure that just because the user made a mistake, that you do not make it worse.