Android activity onKeyDown() not triggered for Power button, and Intent.ACTION_SCREEN_OFF is not broadcasted either

457 views Asked by At

I have an Activity (Activity_RingAlarm) that is supposed to be launched as a full-screen intent when an alarm rings. The activity is launched fine, no issue there. I want to give the user an option to dismiss the alarm by pressing the power button. To this effect, I had programmed the Activity to listen to Power button press, as well as the Intent.ACTION_SCREEN_OFF broadcast. The relevant portion of code is shown below:

public class Activity_RingAlarm extends AppCompatActivity implements View.OnClickListener {

    private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) {
                DisplayManager displayManager = (DisplayManager) context.getSystemService(DISPLAY_SERVICE);
                for (Display display : displayManager.getDisplays()) {
                    if (display.getState() == Display.STATE_OFF) {
                        onPowerButtonPressed();
                    }
                }
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setTurnScreenOn(true);
            setShowWhenLocked(true);
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ringalarm);

        // other codes here

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        registerReceiver(broadcastReceiver, intentFilter);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(broadcastReceiver);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.e(getClass().getSimpleName(), "keycode = " + keyCode);
        if (keyCode == KeyEvent.KEYCODE_POWER) {
            onPowerButtonPressed();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

The issue is, while onKeyDown(int, KeyEvent) responds to volume up and down buttons, it does not respond to the power key. Neither in the emulator, nor in my Samsung Galaxy M31 (both running Android 12 API 31).

I tried putting the above code in the main activity of the app, but even there, the issue is the same.

According to answers posted to this question, it is indeed not possible to capture key events related to the power key, but the ACTION_SCREEN_OFF broadcast should still work. But my app doesn't even get that broadcast. The documentation of the above intent action says that the broadcast has nothing to do with the screen turning off, and is broadcasted when the device becomes non-interactive.

In another question (can't re-locate it at the moment), it was said that dispatchKeyEvent(KeyEvent) should work. I tried that too, but it doesn't.

What can I do in this situation?

1

There are 1 answers

0
Crispert On

What I think happens is the activity is destroyed after you press the power button and the broadcast receiver is unregistered before the screen intents have the chance to arrive. What you could do is change the broadcast receiver to be a top level class and unregister it at a moment that is farther after activity destruction: after a screen intent (ACTION_SCREEN_OFF) is delivered to onReceive, after the alarm is dismissed and/or after a time passes from the activity destruction.

You could also try (if you haven't already) eliminating the window flags from the activity to see if they prevent the intents from being delivered to onReceive().

For debugging consider logging when the receiver is registered/unregistered, all intents delivered to onReceive and adding Intent.ACTION_USER_PRESENT to the IntentFilter.