Hiding an app from recents programmatically

5k Views Asked by At

I want to hide my app from recents programmatically when the user press the home or the task button. The user can decide if the app is persistent in background or not, this setting come from backend so pre-login activities are not to be maintened in recents either case. I tried various solution, some works partially, but none of them work perfectly. I'll list all the methods I used.

1st method. manual task removal: removes every task and kill process

//remove the app from recents. TODO doesn't remove if app list button is pressed instead of home
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                ActivityManager am = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
                if (am != null) {
                    List<ActivityManager.AppTask> tasks = am.getAppTasks();
                    if (tasks != null && !tasks.isEmpty()) {
                        tasks.get(0).setExcludeFromRecents(true);
                    }
                }
            } else {
                //TODO API 19
            }

            //kills the process
            android.os.Process.killProcess(android.os.Process.myPid());

This works fairly well, but not when tasks button is pressed and not for API 19 (which I'm targeting)

2nd method: ExitActivity: first solution found on the web.

public class RemoveFromRecentsActivity extends ApplicationLayer {
    @Override protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        if(android.os.Build.VERSION.SDK_INT >= 21)
        {
            finishAndRemoveTask();
        }
        else
        {
            finish();
        }
    }

    public static void exitApplicationAndRemoveFromRecent(Context context)
    {
        Intent intent = new Intent(context, RemoveFromRecentsActivity.class);

        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK  | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_NO_ANIMATION);

        context.startActivity(intent);
    }
}

that doesn't work because I'm not calling it from onPause(), so it gets executed after the app has gone to background; I'm sure of this because it starts the dummy activity next time I click on the icon or resume it from recents.

3rd method: override startActivity() with excludeFromRecents="true" in first activity (Splash)

so basically I've an activity which extends AppCompatActivityand all my activities extend it. I've overridden startActivity():

@Override
    public void startActivity(Intent intent) {
        if (isUserOptIn){
            if (intent.getComponent().getShortClassName().equals(HomepageActivity.class.getName())){
                intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
            }
        } 
        super.startActivity(intent);
    }

Here I tried to set excludeFromRecents="true" on the splash activity, but if the user has chosen to use the feature, a new task should be created before taking the user to the homepage, but doesn't work. App gets removed from recents anyways.

4th method: override startActivity() with excludeFromRecents="false" in first activity (Splash)

@Override
        public void startActivity(Intent intent) {
            if (!isUserOptIn){

                intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            }
            super.startActivity(intent);
        }

Here I did the opposite, Splash in maintained in recents and EXCLUDE_FROM_RECENTS is added to every new activity, but it doesn't work. When user has not opt-in, the app is not removed from recents. I also tried intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); thinking that the app was not being removed because the first activity had excludeFromRecents="false" but didn't work either.

5th method: override startActivity() with excludeFromRecents="true" in first activity (Splash)

if (! isUserOptIn){
            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS );
        }
        if (intent.getComponent().getShortClassName().equals(HomepageActivity.class.getName())){
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK );
            }

Here I set excludeFromRecents="true" in Splash and tried create a new task when opening HomeActivity regardless of user's choice and add FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS to every activity if necessary. This way the app is always removed. Opposite result if I set excludeFromRecents="false", it is always maintained.

It seems like intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK ); it's not working at all.

Anyone has any idea on how to accomplish this?

2

There are 2 best solutions below

8
David Wasser On

Android will call onUserLeaveHint() if it is about to put the app in the background because the user pressed the HOME or TASK button. You can set a flag in that method. Android will next call onPause(), where you can check the flag and if the flag is set you can immediately call finishAndRemoveTask().

You should implement this behaviuour in all activities (or in a base activity that you derive all other activities from).

NOTE: This only works on API 21 and higher


For API < 21 you can probably simulate finishAndRemoveTask() with something like this:

Instead of calling finishAndRemoveTask(), do this:

Intent clearIntent = new Intent(this, MyRootActivity.class);
clearIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                     Intent.FLAG_ACTIVITY_CLEAR_TASK |
                     Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
clearIntent.putExtra("exit", true);
startActivity(clearIntent);

At the very beginning of onCreate() of your root Activity (the one with ACTION=MAIN and CATEGORY=LAUNCHER), do this:

Intent intent = getIntent();
if (intent.hasExtra("exit")) {
    // finish immediately
   finish();
   return;
}
0
Peter Akwa On

This works for API 19 and above, by using the new ActivityResultLauncher.

First of all, from your main or root activity, define an activity result launcher like below;

ActivityResultLauncher<Intent> startMyActivityForResult = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                //Close and remove the app from recent
                finishAndRemoveTask();
            }
        });

then start your new activity using the ActivityResultLauncher as below;

Intent newActivityIntent = new Intent(this, MyNewActivity.class);
startMyActivityForResult.launch(newActivityIntent);

Then when leaving your new activity back to main and you wish to close the app and remove from recent, then finish the activity with Activity.RESULT_OK as below;

Intent intent1 = new Intent(this, MainActivity.class);
setResult(Activity.RESULT_OK, intent1);
finishAndRemoveTask();

This will automatically close and remove your app from recent.