Search code examples
androidandroid-architecture-navigationandroid-navigation-graph

Android Navigation Component Activity Intent Flags


I have created a navigation action from a fragment to an activity, but I have no way of clearing the back stack. When I execute the navigation action from my fragment to my new activity, and I press the back button, I am taken back to the previous activity and previous fragment. I have no way of setting Intent flags, using the navigation graph, to clear the previous activity from the back stack.

<fragment
    android:id="@+id/loginFragment"
    android:name="com.myapp.auth.LoginFragment"
    android:label="login_fragment"
    tools:layout="@layout/login_fragment" >
    <action
        android:id="@+id/action_loginFragment_to_webActivity"
        app:destination="@id/webActivity"
        app:popUpTo="@id/loginFragment"
        app:popUpToInclusive="true" />
</fragment>
<activity
    android:id="@+id/webActivity"
    android:name="com.myapp.web.WebActivity"
    android:label="activity_web"
    tools:layout="@layout/activity_web" >
</activity>

PopTo and Inclusive flags have no effect on the back button when navigating from a fragment to a new activity, even though they can be set in the graph editor. I am able to navigate, using the back button, to the previous activity that I no longer want in the stack.

Before migrating to the navigation graph, I could just specify this behavior with Intent flags:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

How can I achieve the same thing with the navigation graph?


Solution

  • I had to hack my way through the same problem. To solve this, the first thing you have to do is to create an action to navigate to the Activity, as you already had done.

    For example:

    <action
        android:id="@+id/action_frag_to_myActivity"
        app:destination="@id/myActivity"
        app:popUpTo="@id/myActivity" />
    

    Now, you can pass arguments to the Activity as intent extras, so you can take advantage of that to make the destination Activity do the "dirty work" and clear the back stack for you.

    Say that you have this Activity tag inside your navigation graph:

    <activity
        android:id="@+id/myActivity"
        android:name="com.dummy.MyActivity"
        android:label="activity_my" />
    

    You could add an argument in it and add a default value. For example:

     <activity
        android:id="@+id/myActivity"
        android:name="com.dummy.MyActivity"
        android:label="activity_my">
    
            <argument
                android:name="clearBackstack"
                app:argType="boolean"
                android:defaultValue="true" />
    
    </activity>
    

    Then once you call findNavController().navigate(R.id.myActivity) it'll pass an intent extra with the key "clearBackstack" which you can read inside the Activity onCreate() method. Something like the example below.

    MyActivity.kt

    private val EXTRA_LOGOUT = "clearBackstack"
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (intent.extras?.getBoolean(EXTRA_LOGOUT) == true) {
            clearBackstack()
        } else {
            setContentView(R.layout.activity_my)
        }
    }
    
    private fun clearBackstack() {
        startActivity(Intent(this, MyActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
        })
    
        finish()
    }
    

    Keep in mind you can tinker around with the arguments and customize what you want to do on the destination Activity. You could also modify the value once you navigate to it. You can read more about it here in the docs.