I have a broadcast receiver which receives screen off broadcast, and when the screen is turned off, it launches an Activity. I've named this activity LockScreenActivity. I've added getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
in onCreate()
of the activity, so that it appears on top of the Android lockscreen, which is exactly what I want. This works well enough, mostly. Problem happens whenever I press home button to send any app to the background.
When I press home button to return to home screen from any app, and then immediately press power button to turn off the screen, and press power button in a second to turn in back on, it shows the Android lock screen. My activity gets created some 3-4 seconds later. This is a huge delay and is unacceptable. In other scenarios, this delay does not happen. If I've been in home screen for some time, or some other app is open, and I click power button twice in quick succession, I see my LockScreenActivity before seeing the Android lockscreen.
My app also has a few other normal activities (launched from App drawer) and a service which shows a persistent notification. The same LockScreenActivity is launched whenever the notification is clicked. Interestingly enough, if I press home button to minimize some app and immediately click the notification of my app, it opens the LockScreenActivity without any delay.
I've searched a lot for any solution. Here's what I've tried out so far:
onReceive()
and released it in onResume()
of LockScreenActivityHere's the manifest declaration of the activity, and service:
<activity
android:name=".LockScreenActivity"
android:label="@string/app_name"
android:excludeFromRecents="true"
android:taskAffinity="@string/task_lockscreen">
</activity>
<service
android:name=".services.BaseService"
android:enabled="true" >
</service>
Broadcast receiver:
public class ScreenReceiver extends BroadcastReceiver {
private static final String LOG_TAG = ScreenReceiver.class.getSimpleName();
private static final String HANDLER_THREAD_NAME = "screen_receiver_thread";
public static final String WAKELOCK_TAG = "lockscreen_overlay_create_wakelock";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
WakeLockHelper.acquireWakeLock(WAKELOCK_TAG, context);
Log.d(LOG_TAG, "Screen off");
context.startService(BaseService.getServiceIntent(context, null, BaseService.ACTION_START_LOCKSCREEN_ACTIVITY));
} else {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.d(LOG_TAG, "Screen on");
}
}
}
public static void registerScreenReceiver(Context context){
new BroadcastReceiverRegistration().execute(context);
}
private static class BroadcastReceiverRegistration extends AsyncTask<Context, Void, Void>{
@Override
protected Void doInBackground(Context... params) {
Context context = params[0];
if(Utility.checkForNullAndWarn(context, LOG_TAG)){
return null;
}
HandlerThread handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();
Looper looper = handlerThread.getLooper(); //Will block till thread is started, hence using AsyncTask
Handler handler = new Handler(looper);
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
BroadcastReceiver mReceiver = new ScreenReceiver();
context.registerReceiver(mReceiver, filter, null, handler);
return null;
}
}
}
WakeLockHelper is a class which manages all the wakelocks in my app. registerScreenReceiver()
is called by the Service when it starts.
Here's the onCreate() of LockScreenActivity:
protected void onCreate(Bundle savedInstanceState) {
Log.d(LOG_TAG, "LockscreenActivity onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lock_screen);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new LockScreenFragment())
.commit();
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
getWindow().setDimAmount((float) 0.4);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
int systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
systemUiVisibilityFlags = systemUiVisibilityFlags | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags);
}
Here's the log when things work as expected: (Screen is intially on; it is turned off and on again)
09-19 11:00:09.384 31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen off
09-19 11:00:09.384 31226-31226/com.pvsagar.smartlockscreen D/BaseService﹕ Starting lockscreen overlay.
09-19 11:00:09.394 31226-31226/com.pvsagar.smartlockscreen D/LockScreenActivity﹕ LockscreenActivity onCreate()
09-19 11:00:09.735 31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen on
Not much delay between screen off and sending the intent and the LockScreenActivity actually starting. Now Screen is initially on; I press home from some app (any app, even some other activity of my own app); Screen is turned off and on again:
09-19 11:02:51.557 31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen off
09-19 11:02:51.557 31226-31226/com.pvsagar.smartlockscreen D/BaseService﹕ Starting lockscreen overlay.
09-19 11:02:51.708 31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen on
09-19 11:02:54.851 31226-31226/com.pvsagar.smartlockscreen D/LockScreenActivity﹕ LockscreenActivity onCreate()
Here, as you can see, screen off broadcast is received in time, service gets the intent immediately and send the intent to LockScreenActivity, but onCreate() of LockScreenActivity is delayed by 3 seconds.
This is how the LockScreenActivity is started by the service:
Intent lockscreenIntent = new Intent(this, LockScreenActivity.class);
lockscreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(LOG_TAG, "Starting lockscreen overlay.");
startActivity(lockscreenIntent);
This is the pending intent passed to the notification (just to show that it does the same thing):
Intent notificationIntent = new Intent(context, LockScreenActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notificationBuilder.setContentIntent(pendingIntent);
I'm not having any idea why this is happening. GC is also not running too much. What could be the problem? What can I possibly do to overcome this? Any suggestions/ideas would be greatly appreciated.
The entire code can be found at: https://github.com/aravindsagar/SmartLockScreen/tree/backend_code_strengthening
After much digging, found out the cause of the problem. Apparently it's not a bug, it is a feature which does not allow Services or BroadcastReceivers to launch activities for up to 5 seconds after home button is pressed. No easy way to overcome this.
More info here: https://code.google.com/p/android/issues/detail?id=4536
I replaced the activity with a Window added to the window manager of the running service. This does not cause any delay.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With