68
    <application>
         <receiver android:name=".MyBroadcastReceiver" android:enabled="true">
                <intent-filter>
                      <action android:name="android.intent.action.ACTION_SCREEN_ON"></action>
                      <action android:name="android.intent.action.ACTION_SCREEN_OFF"></action>
                </intent-filter>
         </receiver>
...
    </application>

MyBroadcastReceiver is set just to spit foo to the logs. Does nothing. Any suggestions please? Do I need to assign any permissions to catch the intent?

ohnoes
  • 5,542
  • 6
  • 34
  • 32

6 Answers6

70

I believe that those actions can only be received by receivers registered in Java code (via registerReceiver()) rather than through receivers registered in the manifest.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    OK, I just figured that. What's the rationale behind this? – ohnoes Oct 19 '09 at 12:06
  • 20
    Android does not seem to support manifest-registered receivers for cases where they really do not want to start up a new process. For example, you will see the same effect with the battery info actions (e.g., BATTERY_LOW). Beyond that, though, I don't have much rationale -- I didn't write it. :-) – CommonsWare Oct 19 '09 at 12:50
  • 2
    @CommonsWare So how can i registerReceiver() when Power button is pressed? – Venky Jun 21 '12 at 06:16
  • @CommonsWare Is there a list somewhere of these type of intents that can not be received from manifest, I am kinda facing same problem with CONNECTIVITY_ACTION and PHONE_STATE also. – dirtydexter Nov 24 '14 at 06:27
  • 1
    @dirtydexter: No, there is no such list, and those two broadcasts should be able to be picked up from either the manifest or receivers registered via `registerReceiver()`, AFAIK. – CommonsWare Nov 24 '14 at 11:39
  • So then, how do we know which ones need to be registered programatically? – c0dehunter Sep 17 '18 at 08:37
  • 1
    @PrimožKralj: Well, on Android 8.0+, most have to be registered programmatically -- the exceptions are on [a whitelist](https://developer.android.com/guide/components/broadcast-exceptions). For prior versions of Android, in general, you try it. If it does not work when registered in the manifest, confirm your results by researching the broadcast action in Stack Overflow and similar resources. Ideally, this would have better documentation. – CommonsWare Sep 17 '18 at 22:18
32

Alternatively you can use the power manager to detect screen locking.

@Override
protected void onPause()
{
    super.onPause();

    // If the screen is off then the device has been locked
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    boolean isScreenOn;
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        isScreenOn = powerManager.isInteractive();
    } else {
        isScreenOn = powerManager.isScreenOn();
    }

    if (!isScreenOn) {

        // The screen has been locked 
        // do stuff...
    }
}
Volodymyr Yatsykiv
  • 3,181
  • 1
  • 24
  • 28
Robert
  • 37,670
  • 37
  • 171
  • 213
  • 2
    +1 for approach with `PowerManager` Very nice touch if somebody don't want to use `BroadcastReceiver` – Simon Dorociak Aug 06 '13 at 11:20
  • 3
    just wanted to add that although this will work nicely for 99% of cases out there, this can actually fail in specific circumstances. In devices that can turn the screen on and off very fast, such as Galaxy S4, you can check this behavior failing by combining it with a proximityLock. If you trigger the lock to turn the screen off and back on quickly, isScreenOn will actually return true in onPause(). – i Code 4 Food Jan 24 '14 at 08:25
  • @iCode4Food What you described is right,but how to solve it? – zionpi Jan 25 '16 at 07:31
  • @SimonDorociak where can I add this in a service? (for a service) – Sarah cartenz Jul 19 '18 at 18:59
31
"android.intent.action.HEADSET_PLUG"
"android.intent.action.ACTION_SCREEN_ON"
"android.intent.action.ACTION_SCREEN_OFF"

Three of them above, They cannot register using Manifest. Android core added "Intent.FLAG_RECEIVER_REGISTERED_ONLY" to them (maybe.. I checked codes only in case of "HEADSET_PLUG".

So, We should use "dynamic register". Like below...

private BroadcastReceiver mPowerKeyReceiver = null;

private void registBroadcastReceiver() {
    final IntentFilter theFilter = new IntentFilter();
    /** System Defined Broadcast */
    theFilter.addAction(Intent.ACTION_SCREEN_ON);
    theFilter.addAction(Intent.ACTION_SCREEN_OFF);

    mPowerKeyReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String strAction = intent.getAction();

            if (strAction.equals(Intent.ACTION_SCREEN_OFF) || strAction.equals(Intent.ACTION_SCREEN_ON)) {
                // > Your playground~!
            }
        }
    };

    getApplicationContext().registerReceiver(mPowerKeyReceiver, theFilter);
}

private void unregisterReceiver() {
    int apiLevel = Build.VERSION.SDK_INT;

    if (apiLevel >= 7) {
        try {
            getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
        }
        catch (IllegalArgumentException e) {
            mPowerKeyReceiver = null;
        }
    }
    else {
        getApplicationContext().unregisterReceiver(mPowerKeyReceiver);
        mPowerKeyReceiver = null;
    }
}
cmcromance
  • 2,289
  • 1
  • 25
  • 26
7

The way I implemented this is by registering the receiver in my main activity in onCreate(), just define the receiver somewhere beforehand:

    lockScreenReceiver = new LockScreenReceiver();
    IntentFilter lockFilter = new IntentFilter();
    lockFilter.addAction(Intent.ACTION_SCREEN_ON);
    lockFilter.addAction(Intent.ACTION_SCREEN_OFF);
    lockFilter.addAction(Intent.ACTION_USER_PRESENT);
    registerReceiver(lockScreenReceiver, lockFilter);

And then onDestroy():

    unregisterReceiver(lockScreenReceiver);

In receiver you must catch the following cases:

public class LockScreenReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent != null && intent.getAction() != null)
        {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON))
            {
                // Screen is on but not unlocked (if any locking mechanism present)
            }
            else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
            {
                // Screen is locked
            }
            else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT))
            {
                // Screen is unlocked
            }
        }
    }
}
box
  • 4,450
  • 2
  • 38
  • 38
0

Here is the kotlin version of @cmcromance (Thanks for your answer. Please don't forget to upvote the original answer)

private var mPowerKeyReceiver: BroadcastReceiver? = null

    private fun registBroadcastReceiver() {
        val theFilter = IntentFilter()
        /** System Defined Broadcast  */
        theFilter.addAction(Intent.ACTION_SCREEN_ON)
        theFilter.addAction(Intent.ACTION_SCREEN_OFF)

        mPowerKeyReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {

                Log.e("onReceive", "onReceive called")
                val strAction = intent!!.action

//                if (strAction == Intent.ACTION_SCREEN_OFF || strAction == Intent.ACTION_SCREEN_ON) {
                if (strAction == Intent.ACTION_SCREEN_ON) {
                    // > Your playground~!
                    Log.e("strAction", strAction)
                    val intent = Intent(context, SplashScreenMainAppActivity::class.java)
                    startActivity(intent)
                }

            }
        }

        applicationContext.registerReceiver(mPowerKeyReceiver, theFilter)
    }

    private fun unregisterReceiver() {
        val apiLevel = Build.VERSION.SDK_INT

        if (apiLevel >= 7) {
            try {
                applicationContext.unregisterReceiver(mPowerKeyReceiver)
            } catch (e: IllegalArgumentException) {
                mPowerKeyReceiver = null
            }

        } else {
            applicationContext.unregisterReceiver(mPowerKeyReceiver)
            mPowerKeyReceiver = null
        }
    }
Qadir Hussain
  • 8,721
  • 13
  • 89
  • 124
0

New action key updated!

<intent-filter>
    <action android:name="android.intent.action.SCREEN_ON" />
    <action android:name="android.intent.action.SCREEN_OFF" />
</intent-filter>
Viroth
  • 429
  • 5
  • 14