One of the main benefits of developing applications for Android over iOS is the greater access afforded to some of the lower level API functions. Whilst developing the Dial 9 Phone app I became interested in the proximity sensor.
In Android, the proximity sensor is primarily used to detect when the user’s face is close to the screen. This is how the phone screen seems to know to switch off when you hold it up to your ear during phone calls, preventing any errant button presses. Some Android devices can report the distance to another object in centimetres whereas others will appear to do so but in fact are simply reporting their minimum and maximum values to denote near and far respectively.
The output of the proximity sensor only becomes truly useful when it used in conjunction with other information. This typically includes knowing if a call is currently in progress, locating the source of playback audio and determining the physical orientation of the device, for example, whether it is lying flat on a surface or being held upright.
If you’re particularly interested in this subject the Android source code for the proximity sensor can be viewed here.
Application in visual voicemail
For the Dial 9 Phone app I implemented the use of the proximity sensor in both the call screen and visual voicemail, as you would expect. It was only when I was listening to voicemail and a call came through that I noticed a problem - the audio for the ringtone was routed through the loudspeaker which was currently next to my ear!
I decided that the best approach in this situation was to use the proximity sensor to determine when the user's face is close to the screen and play the softer call waiting tone through the earpiece.
To turn the screen off and on a proximity wake lock is required:
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock proximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "tag");
Let's look at the main methods of a proximity wake lock:
- acquire(), when the proximity wake lock is acquired the screen will turn off whenever the sensor detects that an object is close, turning on again only when this is no longer the case.
- release(optional int flags), when the proximity wake lock is released the screen will turn on immediately unless the optional flag is set to RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY, in which case the screen will turn on when the sensor no longer detects that an object is close.
- isHeld(), if the proximity wake lock has been acquired but not released then it is denoted as held.
As I touched on earlier, acquire() and release() will be called based on a combination of different factors. A typical example is a phone call being in progress with the device in a vertical orientation and audio routed through the earpiece.
A proximity wake lock is used when listening to visual voicemail and if a new call comes through, I query isHeld(). If the method returns true then the call waiting tone is played through the earpiece, whereas, if it returns false then the ringing tone is played through the loudspeaker.
Hopefully, this post has gone some way to demystify a feature that we take for granted in modern smartphones and has piqued your interest in some of lower level API functions in Android.