1

I'm trying to make an app that monitors the users phone usage by tracking time of screen lock and unlock. I tried to setup a BroadcastReceiver which works fine when the app is running the background. But won't work when I close the app. Is there a solution for this.

The code I'm using now is as follows :

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Intent intent = new Intent(this, ScreenListenerService.class);
       startService(intent);
   }

}

ScreenListenerService class is as follows..

public class ScreenListenerService extends Service {

   private BroadcastReceiver mScreenStateBroadcastReceiver = new BroadcastReceiver() {

       @Override
       public void onReceive(Context context, Intent intent) {

           if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {

               // Save something to the server

           } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {

               // Save something to the server

           }

       }

   };

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

   @Override
   public void onDestroy() {
       unregisterReceiver(mScreenStateBroadcastReceiver);
       super.onDestroy();
   }

   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
       super.onStartCommand(intent, flags, startId);
       return START_STICKY;
   }

   @Override
   public IBinder onBind(Intent intent) {
       return null;
   }

}

My AndroidManifest file is as follows :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.abbinvarghese.calculu">

   <application
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:roundIcon="@mipmap/ic_launcher_round"
       android:supportsRtl="true"
       android:theme="@style/AppTheme">
       <service android:name=".ScreenListenerService" />
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
   </application>

</manifest>
plase
  • 361
  • 1
  • 3
  • 9
jelson_jacob
  • 11
  • 1
  • 3

3 Answers3

2

To overcome the imposed limitations of 8.0 you could run a foreground service. Just like a service but a notification is posted to the foreground.

Then the service code would be like this (remember to unregister the receiver onDestory):

BroadcastReceiver screenReceiver;

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    startRunningInForeground();
    detectingDeterminateOfServiceCall(intent.getExtras());
    registerBroadcastReceivers();
    return START_STICKY;
}

private void startRunningInForeground() {

    //if more than or equal to 26
    if (Build.VERSION.SDK_INT >= 26) {

        //if more than 26
        if(Build.VERSION.SDK_INT > 26){
            String CHANNEL_ONE_ID = "sensor.example. geyerk1.inspect.screenservice";
            String CHANNEL_ONE_NAME = "Screen service";
            NotificationChannel notificationChannel = null;
            notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
                    CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_MIN);
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.setShowBadge(true);
            notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            if (manager != null) {
                manager.createNotificationChannel(notificationChannel);
            }

            Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.background_running);
            Notification notification = new Notification.Builder(getApplicationContext())
                    .setChannelId(CHANNEL_ONE_ID)
                    .setContentTitle("Recording data")
                    .setContentText("ActivityLog is logging data")
                    .setSmallIcon(R.drawable.background_running)
                    .setLargeIcon(icon)
                    .build();

            Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
            notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

            startForeground(101, notification);
        }
        //if version 26
        else{
            startForeground(101, updateNotification());

        }
    }
    //if less than version 26
    else{
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("Activity logger")
                .setContentText("data recording on going")
                .setSmallIcon(R.drawable.background_running)
                .setOngoing(true).build();

        startForeground(101, notification);
    }
}

private Notification updateNotification() {

    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
            new Intent(this, MainActivity.class), 0);

    return new NotificationCompat.Builder(this)
            .setContentTitle("Activity log")
            .setTicker("Ticker")
            .setContentText("recording of data is on going")
            .setSmallIcon(R.drawable.activity_log_icon)
            .setContentIntent(pendingIntent)
            .setOngoing(true).build();
}

private void detectingDeterminateOfServiceCall(Bundle b) {
    if(b != null){
        Log.i("screenService", "bundle not null");
        if(b.getBoolean("phone restarted")){
            storeInternally("Phone restarted");
        }
    }else{
        Log.i("screenService", " bundle equals null");
    }
    documentServiceStart();
}


private void documentServiceStart() {
    Log.i("screenService", "started running");
}


private void registerBroadcastReceivers() {
    screenReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (Objects.requireNonNull(intent.getAction())){
                case Intent.ACTION_SCREEN_ON:
                    //or do something else
                    storeInternally("Screen on");
                    break;
                case Intent.ACTION_SCREEN_OFF:
                    //or do something else
                    storeInternally("Screen off");
                    break;
            }
        }
    };

    IntentFilter screenFilter = new IntentFilter();
    screenFilter.addAction(Intent.ACTION_SCREEN_ON);
    screenFilter.addAction(Intent.ACTION_SCREEN_OFF);

    registerReceiver(screenReceiver, screenFilter);
}
@Override
public void onDestroy() {
    super.onDestroy();
    unregisterReceiver(screenReceiver);
}

and call it from the main activity:

private void startServiceRunning() {
    if(!isMyServiceRunning(Background.class)){
        if(Build.VERSION.SDK_INT >25){
            startForegroundService(new Intent(this, Background.class));
        }else{
            startService(new Intent(this, Background.class));
        }
    }
}
KrisG
  • 166
  • 7
1

As Background Execution Limit imposes on Android 8.0 (API level 26) so now it's not possible to listen SCREEN_OFF and SCREEN_ON action in background by running the service.

I have found a work around for same with the help of JobScheduler which works fine for listen broadcast in background without running any service.

Please check on this: Screen OFF/ON broadcast listener without service on Android Oreo

Ankit Kumar Singh
  • 240
  • 1
  • 6
  • 18
0

Instead of creating a new service for broadcast receiver, you can directly create a broadcast receiver class that will listen to system broadcasts even when the app is not running. Create a new class which extends BroadcastReceiver.

public class YourReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //Do your stuff
    }
}

And register it in manifest.

<receiver
    android:name=".YourReceiver"
    android:enabled="true"
    android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.ACTION_SCREEN_ON" />
            <action android:name="android.intent.action. ACTION_SCREEN_OFF" />
            <category android:name="android.intent.category.DEFAUL" />
        </intent-filter>
</receiver>

Read about Manifest-declared receivers here.

Above solution won't work, here is the reason why. Problem is that your service is getting killed when the app is killed, so your receiver instance is removed from memory. Here is a little trick to re-start the service in background. Add the following code to your service.

@Override
public void onTaskRemoved(Intent rootIntent){
    Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
    restartServiceIntent.setPackage(getPackageName());

    PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmService.set(
    AlarmManager.ELAPSED_REALTIME,
    SystemClock.elapsedRealtime() + 1000,
    restartServicePendingIntent);

    super.onTaskRemoved(rootIntent);
 }

Although this is not the right way to do it. Also in Android 26+ you won't be able to do this and you'd go for foreground service. https://developer.android.com/about/versions/oreo/background

Ranjan
  • 1,096
  • 10
  • 22