Search code examples
androiddialogfullscreen

Keep activity always on top of stack, or in focus when rogue apps launch activities?


I have a fullscreen immersive activity that has to stay fullscreen until the user explicitly exits. (For example, think of something like watching a Youtube video full screen).

However, I've noticed recently that a lot of unwanted activities launching can break my app's behaviour. For example, many shady "free apps" tend to generate full-screen transparent activities and show ads, disrupting the user immediately. "Full screen" notification popups from certain apps which are actually full-screen activities also instantly disrupt my activity.

Is there a way to avoid these activities stealing focus, or a way to keep them behind my fullscreen activity so it doesn't break my full screen mode? In other words, how do I keep my activity always on top whenever some rogue app decides to launch an activity over mine?

They don't need to be canceled, but "put behind for the moment" until the user exits the full screen activity.

A method that comes to mind would be to relaunch my activity with FLAG_ACTIVITY_REORDER_TO_FRONT the moment it loses focus, but that won't look pretty to the user :(

Note: If you would like to try it yourself, I've found an app that "simulates" these rogue activities launching. Download this - https://play.google.com/store/apps/details?id=com.nlucas.popupnotificationslite&hl=en

Whenever you receive notifications, it launches a full screen transparent activity. Try watching a Youtube video and getting 10 notifications from someone and imagine how distracting it would be.

UPDATE: Doing this doesn't seem to work:

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (!hasFocus) {
        Intent i = new Intent(getBaseContext(), MainActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
        startActivity(i);
}

Solution

  • You can run a service in the background that checks every so often that will keep your app on top:

    public class ServiceKeepInApp extends Service {
    
    //private static boolean sendHandler = false;
    KeepInAppHandler taskHandler = new KeepInAppHandler(this);
    
    
    @Override
    public void onCreate() {
        super.onCreate();
        //sendHandler = true;
    
        taskHandler.sendEmptyMessage(0);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        //sendHandler = false;
        taskHandler.removeCallbacksAndMessages(0);
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
    
    private static class KeepInAppHandler extends Handler {
        private final WeakReference<ServiceKeepInApp> mService;
        private Context context;
        private boolean sendHandler = false;
    
        public KeepInAppHandler(ServiceKeepInApp mService) {
            this.mService = new WeakReference<ServiceKeepInApp>(mService);
            this.context = mService;
            this.sendHandler = true;
    
        }
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
    
            ActivityManager activityManager = (ActivityManager)context.getSystemService(Service.ACTIVITY_SERVICE);
    
            if (activityManager.getRecentTasks(2, 0).size() > 0) {
                if (activityManager.getRecentTasks(2, 0).get(0).id != GlobalVars.taskID) {
                    activityManager.moveTaskToFront(GlobalVars.taskID, 0);
                }
    
                if (sendHandler) {
                    sendEmptyMessageDelayed(0, 1000);
                }
            }
    
    
        }
    
    }
    
    }
    

    Then have a singleton class:

    public static class GlobalVars { 
       public static int taskID;
       public static Intent keepInApp;
    
    }
    

    Then in your activity:

    public class MyActivity extends Activity { 
    
         @Override
         public void onCreate(Bundle savedInstanceState) {
    
             super.onCreate(savedInstanceState);
    
             GlobalVars.taskID = getTaskId();
    
             GlobalVars.keepInApp = new Intent(this, ServiceKeepInApp.class);
             startService(GlobalVars.keepInApp);
         }
    }