Recognize volume button presses when screen is off Android

2.5k views Asked by At

I am trying to recognize when the user presses the volume rockers when the screen is off to update an activity. From what I've read, BroadcastReceivers (I think) don't work when the phone is asleep, and so the only way to do this is to keep the activity running using a PARTIAL_WAKE_LOCK. My app is a basic one that shouldn't use too much battery, but I'm concerned that using a PARTIAL_WAKE_LOCK might drain the battery (which defies the purpose of recognizing button presses when the screen is off).

Would the PARTIAL_WAKE_LOCK actually be an issue for a basic app? If so, what is the best way to go about doing this, and if not, what is the best way to use the PARTIAL_WAKE_LOCK (i.e. when should I acquire/release it)?

Thanks in advance.

3

There are 3 answers

1
Colt McAnlis On

There's two questions here; I'm going to focus on the "WAKELOCK' portion of it.

Wake Locks are notorious for churning through the battery of a mobile device. Understand that the device will dim the screen, then turn off the screen before turning off the CPU and entering "sleep" mode. This is an extremely important step for your phone, since it conserves battery.

Wake locks are extremely dangerous, because it's too easy to forget to release the lock when you're done.

In general, you shouldn't try to keep the device awake when it's trying to sleep; This will infuriate your users as you're eating up their battery.

Here's some tips to maybe handle wake locks in a better way:

1) Only allow this operation if the user is docked/ charging

2) Use the version of WakeLock.aquire(..) that accepts a timeout parameter. This way, you can wake up, check for some states, and then go back to sleep automatically if nothing is happening. (see http://goo.gl/FkrO8)

3) Use AlarmManager to create in-exact timers; This will allow your app to be woken up when other apps have woken up to do work as well.

4) Use the JobScheduler API (released in L) which will allow you to handle this type of scheduling, and work deferring, from a single API. (see http://goo.gl/Z1AqSn)

0
optedoblivion On

This is going to be almost impossible since this is handled at the window manager layer. See this patch for an example of enabling it from the OS side of things. It requires modifying things you don't have access to as a third party developer. Otherwiae you will have to hold a wake lock. You will see as was mentioned about dock/charge in the commit where it handles that as well.

http://review.cyanogenmod.org/#/c/80351/

0
Hpsaturn On

I had some troubles with this, and also around in the order to perform the wakeLock calls. The final setup to have buttons detection and the screen off, was using different wakeLocks, for example in a call app, I did the next implementation using the proximity sensor to turnOn/turnoff the screen, but also enable the capacity to detect the buttons when the screen is off or the app is in background:

Instance:

PowerManager powerManager = (PowerManager) getSystemService(Activity.POWER_SERVICE);     
proximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, "call:proximity");
partialWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"call:partial");

Implementation:

class ProximityListener implements SensorEventListener {
        private boolean mActive=false;

        @SuppressLint("WakelockTimeout")
        public void setActive(boolean b) {
            mActive=b;
            if( mActive ) {
                sensorManager.registerListener(this, proximitySensor, Sensor.TYPE_PROXIMITY);
                if (!proximityWakeLock.isHeld()) {
                    partialWakeLock.acquire();
                    proximityWakeLock.acquire();
                }
            } else {
                sensorManager.unregisterListener(this, proximitySensor);
                if (proximityWakeLock.isHeld()) {
                    partialWakeLock.release();
                    proximityWakeLock.release();
                }
                if( getWindow()!=null ) {
                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                }
            }
        }

        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.values[0] == 0.0) {
                turnOffScreen();
            } else if (event.values[0] > 0.0) {
                turnOnScreen();
            }
        }
    }