Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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);
}
like image 607
Kyle Avatar asked Feb 07 '16 17:02

Kyle


People also ask

Where would we specify which activity should launch first in app?

The intent-filter inside the activity tells Android which Activity to launch.

Does finish remove activity from stack?

The back button (by default) then 'pops' the stack, calling finish() on the topmost activity, destroying it and removing it from the back stack and taking you back to the previous activity.

What is activity stack in mobile application development?

A task is a collection of activities that users interact with when trying to do something in your app. These activities are arranged in a stack—the back stack—in the order in which each activity is opened. For example, an email app might have one activity to show a list of new messages.


1 Answers

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);
     }
}
like image 175
Kristy Welsh Avatar answered Oct 23 '22 06:10

Kristy Welsh