Android Navigation Component Activity Intent Flags
Asked Answered
H

5

7

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?

Happenstance answered 7/1, 2020 at 17:43 Comment(0)
V
3

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.

Vicariate answered 17/1, 2020 at 22:28 Comment(6)
is there any other solutions beside this one works though only thing is a second or 2 blacked out screen when activity is started that too is understandable as clearbackstack takes to re start the activity.Allium
Man thats painful. I think we need an Android RFE for this one.Irredeemable
It is painful indeed @BrillPappin :/ I wish they thought of a better way to handle those scenarios. But hey, at least the hack works.Vicariate
@Vicariate good news. I noticed working with navigation yesterday, that you actually can control it. In my case i had a global action, and when selected in the editor UI, there were the options for single top, etc. So, the actions can have app:launchSingleTop="true", and another setting for how to pop the activity. They only seem to be on the action though.Irredeemable
But does it solve this specific use case tho? IIRC I tried this before and it didn't quite workVicariate
ActivityNavigator.Extras could be used to set intent flags. No need to relaunch the activityHemp
O
7

Referencing Fatih's answer

val extras = ActivityNavigator.Extras.Builder()
        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
        .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        .build()
findNavController().navigate(BlaBlaActivityDirections.actionFooBarToBlaBlaActivity(), extras)

Note You need to add these flags to the activity stack:

  1. FLAG_ACTIVITY_CLEAR_TASK: sets the activity as the root task. used with
  2. FLAG_ACTIVITY_NEW_TASK: treats the activity as launcher activity.
  3. FLAG_ACTIVITY_REORDER_TO_FRONT: brings the activity to front. Use this only if you have other activities on the stack and you don't want to clear it.
Outbreak answered 24/8, 2020 at 18:8 Comment(1)
Did this stop working at some version? Adding the combination of FLAG_ACTIVITY_CLEAR_TASK and FLAG_ACTIVITY_NEW_TASK makes the new activity to be recreated then cleared multiple times... Anyone got this working on the new versions of Navigation Component?Embolus
V
3

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.

Vicariate answered 17/1, 2020 at 22:28 Comment(6)
is there any other solutions beside this one works though only thing is a second or 2 blacked out screen when activity is started that too is understandable as clearbackstack takes to re start the activity.Allium
Man thats painful. I think we need an Android RFE for this one.Irredeemable
It is painful indeed @BrillPappin :/ I wish they thought of a better way to handle those scenarios. But hey, at least the hack works.Vicariate
@Vicariate good news. I noticed working with navigation yesterday, that you actually can control it. In my case i had a global action, and when selected in the editor UI, there were the options for single top, etc. So, the actions can have app:launchSingleTop="true", and another setting for how to pop the activity. They only seem to be on the action though.Irredeemable
But does it solve this specific use case tho? IIRC I tried this before and it didn't quite workVicariate
ActivityNavigator.Extras could be used to set intent flags. No need to relaunch the activityHemp
I
0

You can control properties like singleTop from the action element, in the navigation definition.

The properties are available in the navigation editor as well.

enter image description here

Irredeemable answered 15/5, 2020 at 21:1 Comment(0)
H
0

You can add an intent flag to navigator extras.

val extras = ActivityNavigator.Extras.Builder()
   // Add your flags 
   .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
   .build()

// Navigate with extras 
findNavController().navigate(destination, extras)

You can check how the intent flags are handled in ActivityNavigator.navigate()from the source code

Alternatively, you can use navigation options if the only flag you will set is FLAG_ACTIVITY_SINGLE_TOP.

val navOptions = NavOptions.Builder().setLaunchSingleTop(true).build()
findNavController().navigate(destination, navOptions)

You can check how nav options are checked in ActivityNavigator.navigate()from the source code

Hemp answered 30/7, 2020 at 9:0 Comment(0)
H
-4

If you are moving from one activity to another.

That's how I solved the same question. I hope it helps.

firstIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Himeji answered 7/1, 2020 at 19:40 Comment(1)
That's how I solved the same question I seriously doubt that you have used the navigation componentEdita

© 2022 - 2024 — McMap. All rights reserved.