Search code examples
androidvolumeonkeydown

Pressing Volume down calls the onKeyDown late (While Volume up calls it in time)


I've implemented onKeyDown and onKeyUp in a very simple activity, and noticed weird behavior with the Volume Down key.

Here is the code:

    public boolean onKeyDown(int keyCode, KeyEvent event) 
{
    if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN))
    {
        B9=true;
        Log.w("Alon", "Volume_down Down "+SystemClock.elapsedRealtime());
    }
    else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP))
    {
        B10=true;
        Log.w("Alon", "Volume_up Down "+SystemClock.elapsedRealtime());
    }
    else 
           return super.onKeyDown(keyCode, event);

    return true;
}
public boolean onKeyUp (int keyCode, KeyEvent event) 
{
    if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN))
    {
        B9=false;
        Log.w("Alon", "Volume_down Up "+SystemClock.elapsedRealtime());
    }
    else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP))
    {
        B10=false;
        Log.w("Alon", "Volume_up Up "+SystemClock.elapsedRealtime());
    }
    else 
       return super.onKeyDown(keyCode, event);
    return true;
}

Clicking Volume up and letting go as fast as humanely possible produces correct results:

12-15 17:43:22.215: W/Alon(751): Volume_up Down 74650325
12-15 17:43:22.265: W/Alon(751): Volume_up Up 74650374
12-15 17:43:22.705: W/Alon(751): Volume_up Down 74650818
12-15 17:43:22.765: W/Alon(751): Volume_up Up 74650878
12-15 17:43:23.566: W/Alon(751): Volume_up Down 74651675
12-15 17:43:23.656: W/Alon(751): Volume_up Up 74651768
12-15 17:43:24.107: W/Alon(751): Volume_up Down 74652222
12-15 17:43:24.177: W/Alon(751): Volume_up Up 74652284

Doing the same with Volume Down does not:

12-15 17:44:13.139: W/Alon(751): Volume_down Down 74701252
12-15 17:44:13.139: W/Alon(751): Volume_down Up 74701253
12-15 17:44:13.840: W/Alon(751): Volume_down Down 74701950
12-15 17:44:13.840: W/Alon(751): Volume_down Up 74701950
12-15 17:44:14.751: W/Alon(751): Volume_down Down 74702863
12-15 17:44:14.761: W/Alon(751): Volume_down Up 74702865
12-15 17:44:15.431: W/Alon(751): Volume_down Down 74703546
12-15 17:44:15.431: W/Alon(751): Volume_down Up 74703547
12-15 17:44:16.673: W/Alon(751): Volume_down Down 74704785
12-15 17:44:16.673: W/Alon(751): Volume_down Up 74704785

I tested and got the same results on two phones - Nexus 4 and Galaxy S. Is this a known issue in Android? Am I doing something wrong?


Solution

  • VOLUME_DOWN + POWER_KEY is a special key combination triggering a screenshot.

    When the VOLUME_DOWN key goes down, the event is intercepted before dispatching by the PhoneWindowManager.

    The PhoneWindowManager tells to the dispatcher to wait a little (150 ms) before dispatching just in case a screenshot is triggered by the user... and that's what the dispatcher do : he wait...

    ...then the VOLUME_DOWN key goes up. The dispatcher can dispatch the VOLUME_DOWN up event immediately (i.e. in this case: the PhoneWindowManager don't have any reason to tell to the dispatcher to wait).

    But : there is the pending VOLUME_DOWN down in the queue. So the dispatcher dispatch the VOLUME_DOWN down and immediately after the VOLUME_DOWN up.

    Some code from PhoneWindowManager:

        // If we think we might have a volume down & power key chord on the way
        // but we're not sure, then tell the dispatcher to wait a little while and
        // try again later before dispatching.
        if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
            if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) {
                final long now = SystemClock.uptimeMillis();
                final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
                if (now < timeoutTime) {
                    return timeoutTime - now;
                }
            }
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mVolumeDownKeyConsumedByScreenshotChord) {
                if (!down) {
                    mVolumeDownKeyConsumedByScreenshotChord = false;
                }
                return -1;
            }
        }