1

I'm working on an open source app, and these are my requirements for when the root activity is launched:

  1. App needs to always be single task to prevent invalid states occurring via some shared data

  2. When started via launch intents, user should go back to the Activity at top of any previous task

  3. When launched via ACTION_VIEW (e.g. via file manager) user should be taken to the root activity, all previous history cleared, and the intent handled properly by importing the file.

Clearly requirement 2 prohibits using android:launchMode="singleTask" in manifest, and I have no control over what flags are included in the external intent.

I can handle requirements 1 and 2 by finishing the activity if !isTaskRoot(). Handling requirement 3 is trickier; after much strife I have found that relaunching the intent with the inclusion of the flag FLAG_ACTIVITY_CLEAR_TOP kind of works, with the caveat that isTaskRoot() doesn't seem to return true until the other Activity has finished, so I had to introduce a hack to see if the Activity is going to be root soon, or else the app freezes for several seconds before proceeding.

Anyway, here's the code; any less hacky solution satisfying all requirements would be much appreciated.

@Override
protected void onCreate(Bundle savedInstanceState) {
    Intent intent = getIntent();
    if (!isTaskRoot() && 
        (intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) == 0) {
        // App freezes for a while without hack in second condition above
        super.onCreate();
        Intent reloadIntent = new Intent(RootActivity.this, RootActivity.class);
        if (intent.getExtras() != null) {
            reloadIntent.putExtras(intent.getExtras());
        }
        if (intent.getData() != null) {
            reloadIntent.setData(intent.getData());
        }            
        String action = intent.getAction();            
        switch (action) {
            case Intent.ACTION_VIEW:
                // Go back to previous task & take user to root, clearing history
                reloadIntent.setAction(intent.getAction());
                reloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                finish();
                startActivity(reloadIntent);
                break;
            default :
                // Go back to the top of previous task without clearing history
                reloadIntent.setAction(Intent.ACTION_MAIN);
                reloadIntent.addCategory(Intent.CATEGORY_LAUNCHER);
                reloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                finish();
                startActivityIfNeeded(reloadIntent, 0);
                break;
        }
    } else {        
        // Normal onCreate() method for when launched as root
        // Import file specified by intent.getData().getEncodedPath()
    }
}
Community
  • 1
  • 1
Tim Rae
  • 3,167
  • 2
  • 28
  • 35

1 Answers1

1

A much better solution than the hack in the question is to use a separate activity as a delegate for Intents. Here is the final solution posted verbatim from the project source code:

public class IntentHandler extends Activity {
    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        Intent reloadIntent = new Intent(this, DeckPicker.class);
        reloadIntent.setDataAndType(intent.getData(), intent.getType());
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_VIEW)) {
            // This intent is used for opening apkg package
            // We want to go immediately to DeckPicker, clearing any history in the process
            // TODO: Still one bug, where if AnkiDroid is launched via ACTION_VIEW,
            // then subsequent ACTION_VIEW events bypass IntentHandler. Prob need to do something onResume() of AnkiActivity
            reloadIntent.setAction(action);
            reloadIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(reloadIntent);
            finish();
        } else {
            // Launcher intents should start DeckPicker if no other task exists,
            // otherwise go to previous task
            reloadIntent.setAction(Intent.ACTION_MAIN);
            reloadIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            reloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivityIfNeeded(reloadIntent, 0);
            finish();
        }
    }
}
Tim Rae
  • 3,167
  • 2
  • 28
  • 35