Skip to content Skip to sidebar Skip to footer

Screen On/off Broadcast Listener For A Widget On Android Oreo

I have a clock widget Android app which I am now trying to update to API 26 requirements. Up to now I used a background service which registered upon start in its onCreate method a

Solution 1:

Here what I am doing for listen SCREEN_OFF and SCREEN_ON broadcast in Android API 26 (Oreo) and above. This answer is not related to widget but it may be helpful to find some work-around.

I am using Job Scheduler for register and unRegister Broadcast Receiver which listen SCREEN_OFF and SCREEN_ON action.

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.util.Log;

import com.evernote.android.job.Job;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;

import java.util.concurrent.TimeUnit;


publicclassLockScreenJobextendsJob {

    privatestaticfinalStringTAG= LockScreenJob.class.getSimpleName();

    publicstaticfinalStringTAG_P="periodic_job_tag";
    publicstaticfinalStringTAG_I="immediate_job_tag";

    //Used static refrence of broadcast receiver for ensuring if it's already register or not NULL// then first unregister it and set to null before registering it again.publicstaticUnlockReceiveraks_Receiver=null;

    @Override@NonNullprotected Result onRunJob(Params params) {
        // run your job hereStringjobTag= params.getTag();

        if (BuildConfig.DEBUG) {
            Log.i(TAG, "Job started! " + jobTag);
        }

        PowerManagerpm= (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);

        booleanisInteractive=false;
        // Here we check current status of device screen, If it's Interactive then device screen is ON.if (Build.VERSION.SDK_INT >= 20) {
            isInteractive = pm.isInteractive();
        } else {
            isInteractive = pm.isScreenOn();
        }

        try {
            if (aks_Receiver != null) {
                getContext().getApplicationContext().unregisterReceiver(aks_Receiver); //Use 'Application Context'.
            }
        } catch (Exception e) {
            if (BuildConfig.DEBUG) {
                e.printStackTrace();
            }
        } finally {
            aks_Receiver = null;
        }

        try {
            //Register receiver for listen "SCREEN_OFF" and "SCREEN_ON" action.IntentFilterfilter=newIntentFilter("android.intent.action.SCREEN_OFF");
            filter.addAction("android.intent.action.SCREEN_ON");
            aks_Receiver = newUnlockReceiver();
            getContext().getApplicationContext().registerReceiver(aks_Receiver, filter); //use 'Application context' for listen brodcast in background while app is not running, otherwise it may throw an exception.
        } catch (Exception e) {
            if (BuildConfig.DEBUG) {
                e.printStackTrace();
            }
        }

        if (isInteractive)
        {
          //TODO:: Can perform required action based on current status of screen.
        }

        return Result.SUCCESS;
    }

    /**
     * scheduleJobPeriodic: Added a periodic Job scheduler which run on every 15 minute and register receiver if it's unregister. So by this hack broadcast receiver registered for almost every time w.o. running any foreground/ background service. 
     * @return
     */publicstaticintscheduleJobPeriodic() {
        intjobId=newJobRequest.Builder(TAG_P)
                .setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
                .setRequiredNetworkType(JobRequest.NetworkType.ANY)
                .build()
                .schedule();

        return jobId;
    }

    /**
     * runJobImmediately: run job scheduler immediately so that broadcasr receiver also register immediately
     * @return
     */publicstaticintrunJobImmediately() {
        intjobId=newJobRequest.Builder(TAG_I)
                .startNow()
                .build()
                .schedule();

        return jobId;
    }

    /**
     * cancelJob: used for cancel any running job by their jobId.
     * @param jobId
     */publicstaticvoidcancelJob(int jobId) {
        JobManager.instance().cancel(jobId);
    }
}

And my JobCrator class LockScreenJobCreator is:

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.evernote.android.job.Job;
import com.evernote.android.job.JobCreator;

publicclassLockScreenJobCreatorimplementsJobCreator {

    @Override@NullablepublicJobcreate(@NonNullString tag) {
        switch (tag) {
            caseLockScreenJob.TAG_I:
                returnnewLockScreenJob();
            caseLockScreenJob.TAG_P:
                returnnewLockScreenJob();
            default:
                returnnull;
        }
    }
}

BroadcastReceiver class UnlockReceiver is :

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

publicclassUnlockReceiverextendsBroadcastReceiver {

    privatestaticfinalStringTAG= UnlockReceiver.class.getSimpleName();

    @OverridepublicvoidonReceive(Context appContext, Intent intent) {

        if (BuildConfig.DEBUG) {
            Log.i(TAG, "onReceive: " + intent.getAction());
        }

        if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF))
        {
          //TODO:: perform action for SCREEN_OFF
        } elseif (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) {
          //TODO:: perform action for SCREEN_ON
        }
    }

}

And adding JobCreator class to the Application class like this:

publicclassAksApplicationextendsApplication {

    @OverridepublicvoidonCreate() {
        super.onCreate();

       JobManager.create(this).addJobCreator(newLockScreenJobCreator());   

       //TODO: remaing code
    }

}

Don't forget to define application class in your AndroidManifest.xml

After all this I start Job scheduler from my Activity like this:

import android.support.v7.app.AppCompatActivity;

publicclassLockScreenActivityextendsAppCompatActivity {

    @OverridepublicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        runJobScheduler();

        //TODO: other code
    }

    @OverrideprotectedvoidonStop() {
      super.onStop();

      cancelImmediateJobScheduler();

      //TODO: other code
    }

    /**
     * runJobScheduler(): start immidiate job scheduler and pending job schedulaer from 
       your main Activity.
     */privatevoidrunJobScheduler() {
        Set<JobRequest> jobSets_I = null, jobSets_P = null;
        try {
            jobSets_I = JobManager.instance().getAllJobRequestsForTag(LockScreenJob.TAG_I);
            jobSets_P = JobManager.instance().getAllJobRequestsForTag(LockScreenJob.TAG_P);

            if (jobSets_I == null || jobSets_I.isEmpty()) {
                LockScreenJob.runJobImmediately();
            }
            if (jobSets_P == null || jobSets_P.isEmpty()) {
                LockScreenJob.scheduleJobPeriodic();
            }

            //Cancel pending job scheduler if mutiple instance are running.if (jobSets_P != null && jobSets_P.size() > 2) {
                JobManager.instance().cancelAllForTag(LockScreenJob.TAG_P);
            }
        } catch (Exception e) {
            if (Global_Var.isdebug) {
                e.printStackTrace();
            }
        } finally {
            if (jobSets_I != null) {
                jobSets_I.clear();
            }
            if (jobSets_P != null) {
                jobSets_P.clear();
            }
            jobSets_I = jobSets_P = null;
        }
    }


    /**
     * cancelImmediateJobScheduler: cancel all instance of running job scheduler by their 
      TAG name. 
     */privatevoidcancelImmediateJobScheduler() {  
            JobManager.instance().cancelAllForTag(LockScreenJob.TAG_I);
    }
}

By running Job Scheduler like this I am able to listen SCREEN_OFF and SCREEN_ON action without running any foreground or background service. I tested above code on API 26+ and it's working fine.

Post a Comment for "Screen On/off Broadcast Listener For A Widget On Android Oreo"