Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starting a service when the device's screen is unlocked in Android

I would like to run my service when the screen is unlocked and stop it when the screen is locked. I looked into these answers and implemented them. However, when I lock my screen the service stops as required, but when I unlock the screen it doesn't starts again.

The code for to run this service is as follows:

public class PhonePositionService extends Service {
@Override
    public void onCreate() {
    //ADDED CODE
    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    BroadcastReceiver mReceiver = new BootCompletedIntentReceiver();
    registerReceiver(mReceiver, filter);
    {...}
    }
@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    int icon = R.drawable.ic_stat_bright;
    startForeground(NOTIFICATION_ID, getCompatNotification(icon));
    if (backgroundThread != null) {
      backgroundThread.start();
    }
    return START_STICKY;
  }
}

In some other file, my broadcast receiver code is as follows:

public class BootCompletedIntentReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   if(Intent.ACTION_SCREEN_ON.equals(action)) {
     // start the service
     Intent pushIntent = new Intent(context, PhonePositionService.class);
     context.startService(pushIntent);
   } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {
     // stop the service
     Intent pushIntent = new Intent(context, PhonePositionService.class);
     context.stopService(pushIntent);
   }
  if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
    Intent pushIntent = new Intent(context, PhonePositionService.class);
    context.startService(pushIntent);
  }
 }
}

Then why is it so that when I unlock my phone the service does not start again?

like image 429
Sarah cartenz Avatar asked Apr 10 '18 05:04

Sarah cartenz


People also ask

How do you unlock a locked Android screen?

How to remove Screen Lock on your Android phone. Tap Settings > Security > Screen Lock. If prompted, enter your current lock screen code > None > Delete.

What do I do when my Android phone is locked?

After you've tried to unlock your phone multiple times, you'll see "Forgot pattern." Tap Forgot pattern. Enter the Google Account username and password you previously added to your phone. Reset your screen lock. Learn how to set a screen lock.

How to disable lock task mode?

If an activity previously called startLockTask() , then the activity can call Activity. stopLockTask() to stop lock task mode.

How do you check phone is locked or unlocked in Android programmatically?

isDeviceLocked to detect if the device is currently locked and requires a PIN, pattern or password to unlock.


2 Answers

First of all its not possible to detect if user has unlocked the phone until you have Admin Privilages. The issue you are facing is because you are stopping the PhonePositionService using stopService() when Screen is OFF:

public class BootCompletedIntentReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   if(Intent.ACTION_SCREEN_ON.equals(action)) {
     // start the service
     Intent pushIntent = new Intent(context, PhonePositionService.class);
     context.startService(pushIntent);
   } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {
     // stop the service
     Intent pushIntent = new Intent(context, PhonePositionService.class);
     //This will stop the service
     context.stopService(pushIntent);
   }
  if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
    Intent pushIntent = new Intent(context, PhonePositionService.class);
    context.startService(pushIntent);
  }
 }
}

It would lead to some memory leak since BroadcastReceiver is not unregistered.

It is discouraged to keep service running in foreground for forever since it could lead to (fast) battery drainage.

I would recommend you to find other alternatives. But if your core functionality is impacted and you still want to proceed you can do following work around:

Make BootCompletedIntentReceiver as inner class for PhonePositionService. And instead of starting and stopping the service you perform your action directly.

public class PhonePositionService extends Service {
   @Override
   public void onCreate() {
       //ADDED CODE
       IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
       filter.addAction(Intent.ACTION_SCREEN_OFF);
       BroadcastReceiver mReceiver = new BootCompletedIntentReceiver();
       registerReceiver(mReceiver, filter);
       ...
    }
    ...
    private class BootCompletedIntentReceiver extends BroadcastReceiver {
       @Override
       public void onReceive(Context context, Intent intent) {
         String action = intent.getAction();
         if(Intent.ACTION_SCREEN_ON.equals(action)) {
          //DO action for SCREEN_ON
         } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {
          //Do action for SCREEN_OFF
         }

       }
   }
}
like image 50
Sagar Avatar answered Oct 09 '22 19:10

Sagar


That's because you are registering your receiver inside your service, which is a component like an activity that has a lifecycle and when it's destroyed your broadcast listener goes with it too. Actually I'm guessing you are leaking your Service here too, because you didn't unregister it which is another problem.

Since API 26 aka Oreo you cannot register you broadcast listener inside AndroidManifest.xml, If you want to target API 26+ your best bet is to register it in your Application class, but be aware when the system kills your app you will lose your broadcast listener too. However if you use a foreground service the system won't kill your service.

Edit: Here is an example:

import android.app.Application;
import android.content.Intent;
import android.content.IntentFilter;

/**
 * @author : [email protected]
 * Created on: 2018-04-12
 */
public class App extends Application {

    @Override
    public void onCreate() {
    super.onCreate();
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_SCREEN_ON);
    registerReceiver(new BootCompletedIntentReceiver(), filter);
    }
}

Don't forget to set this class inside your manifest file.

<application
    android:name=".App"
    android:allowBackup="true"

Note you need a foreground service to be run always to not miss the events.

PS: There's another solution which is a bit hacky, target a lower API version and register your listener inside the manifest file.

Looks like targeting a lower API is out of table. It seems google knew some developers would use it to get around this.

like image 25
M. Reza Nasirloo Avatar answered Oct 09 '22 19:10

M. Reza Nasirloo