Keep activity always on top of stack, or in focus

2019-06-14 23:54发布

问题:

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);
}

回答1:

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);
     }
}


回答2:

Your best bet is to use "screen pinning" (as introduced here, new in Android L). Anything else is a hack or workaround that will not be forward compatible.

To be blunt, Android was not really designed to allow an app to unconditionally and fully take over a device. Android was designed to be a tool for the user that responds to their wishes. If their wish is to leave an app, why should it make that difficult? Also, if the user wishes to have shady apps that unexpected start activities, then so be it. Everyone plays by the same rules established by the system.