I've met a problem with the back stack while starting the app from a deep link (via notification).
The general idea is that I have 3 fragments (A, B, C). The A screen works as splash screen, so I've put a popUpTo and popUpToInclusive to the action, so I can never see back to it again. My deep link destination is fragment C. To achieve the proper back stack I've merged B and C into nested navigation. Unfortunately, when I press Back button in fragment B the app is showing fragment A. My guess this is because the action from A to B with popUpTo was never called when the app was started from the deep link itself. My question is: how to avoid going back from B to A in this particular scenario?
Thanks in advance! I attach the sample code:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<navigation android:id="#+id/nested_nav"
app:startDestination="#id/B">
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
</navigation>
And my DeepLinkBuilder (nothing special, same as the docs)
NavDeepLinkBuilder(requireContext())
.setGraph(R.navigation.nav_graph)
.setDestination(R.id.userProfileFragment)
.createPendingIntent()
.send()
You have set the navGraph that contain the splash screen as the default startDestination which lead to show it when you press back button from the nested navGraph , I think the easiest solution is to do the following :
First edit you nav file to have only one navigation like :
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
Secondly set a listener to navController to listen when a destination change occur , like this :
yourNavController.setOnDestinationChangedListener(this);
override onDestinationChangedListener() method and check if the fragment which about to get showed is A , then finish the app like :
#override
public void onDestinationChanged(NavController controller, NavDestination destination, Bundle arguments){
if(destination.getId()==R.id.splashFragmentId){
finish();
}
}
Related
So I have an androidx.fragment.app.Fragment (A) that navigates to androidx.preference.PreferenceFragmentCompat (B) using:
findNavController().navigate(ADirections.actionAToB())
B then navigates to another androidx.preference.PreferenceFragmentCompat (C) using:
findNavController().navigate(BDirections.actionBToC())
In C I have a button that when clicked should navigate back (in the backstack?) to B. Now C should not exist any more (the data represented by C is destroyed).
On C:s clicklistener I have tried (without success):
findNavController().navigateUp()
findNavController().navigate(CDirections.actionCToB())
findNavController().popBackStack()
this#C.parentFragmentManager.popBackStack()
All of those options navigate me to A instead of B.
The back arrow (the OS back option) works when in C by going back to B.
------ Edit Add nav graph ---------
In my question I missed one level of settings views but the problem is the same ... A is a Fragment, B is a PreferenceFragmentCompat, C is a PreferenceFragmentCompat and then D is a PreferenceFragmentCompat and the place I try to navigate from to C (rather then B as stated above)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/A">
<fragment
android:id="#+id/A"
android:name="my.package.A"
android:label="A"
tools:layout="#layout/A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B" />
</fragment>
<fragment
android:id="#+id/B"
android:name="my.package.sub.B"
android:label="B" >
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="my.package.sub.C" >
<action
android:id="#+id/action_C_to_D"
app:destination="#id/D" />
</fragment>
<fragment
android:id="#+id/D"
android:name="my.package.sub.D"
android:label="D" />
</navigation>
I am using navigation architecture component in my application.
Suppose I have fragment A as start destination.
I have push fragment B and Fragment C via navigation controller.
My navigation back stack is A-> B-> C.
Then I navigate to Fragment A with below action
<action
android:id="#+id/fragment_A_type"
app:destination="#id/fragment_a"
app:popUpTo="#id/fragment_a"
app:popUpToInclusive="true" />
Now My navigation back stack should be A.
But if check
parentFragmentManager.fragments back stack is
A->A
Also the swipeBackDismiss of activity is not working.
my navigation.xml file
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph">
<fragment
android:id="#+id/FirstFragment"
android:name="com.example.wearexample1.FirstFragment"
android:label="#string/first_fragment_label"
tools:layout="#layout/fragment_first">
<action
android:id="#+id/action_FirstFragment_to_SecondFragment"
app:destination="#id/SecondFragment" />
</fragment>
<fragment
android:id="#+id/SecondFragment"
android:name="com.example.wearexample1.SecondFragment"
android:label="#string/second_fragment_label"
tools:layout="#layout/fragment_second">
</fragment>
<action
android:id="#+id/action_global_activity_type"
app:destination="#id/FirstFragment"
app:popUpTo="#id/FirstFragment"
app:popUpToInclusive="true" />
</navigation>
I'm using Android Jetpack Navigation Component for my app. It has Two screen (Fragment) which are homeFragment and loginFragment. The homeFragment is the start destination. After logout from homeFragment to loginFragment, the navigation back button shows up, it will back to homeFragment again when click, that's not correct. I expect the back button not shown in loginFragment, and if user press system back app exit. The nav_graph.xml is setting properly as this post was told:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_graph"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/loginFragment"
android:name="cn.helptool.qxscales.LoginFragment"
android:label="Login" >
<action
android:id="#+id/action_loginFragment_to_homeFragment"
app:destination="#id/homeFragment"
app:popUpTo="#id/nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/homeFragment"
android:name="cn.helptool.qxscales.HomeFragment"
android:label="Home">
<action
android:id="#+id/action_homeFragment_to_loginFragment"
app:destination="#id/loginFragment"
app:popUpTo="#id/nav_graph"
app:popUpToInclusive="true" />
</fragment>
</navigation>
After read android docs
Add both of loginFragment and homeFragment as top level destinations, the problem fixed.
val appBarConfiguration = AppBarConfiguration.Builder(R.id.loginFragment, R.id.homeFragment).build()
setupActionBarWithNavController(navController, appBarConfiguration)
I am using Navigation Component to build my application. I would like to implement SingleTask launch mode in the fragment navigation how can I achieve it?
For more clarity, I will detail my problem.
I have a HomeFragement(Lets Fragment A) and from that I have defined actions to the inner screens
Fragment A > Fragment B > Fragment C > Fragment D
I have a BaseFragment which extends all the above fragments in which I have implemented back click and its action using findNavController().popBackStack()
In Fragment D when the user clicks the back button as expected, it is navigating back to Fragment C. My problem comes when I have called an action whose destination fragment is Fragment A. I am calling this action on a successful event in Fragment D, that action also works well. But when the user press the back button from Fragment A it goes to Fragment C, the next back click goes to Fragment B, then the next back click to FRagment A. I should destroy Fragment B and Fragment C on the successful event called in Fragment D and should resume Fragment A.
I know this flow can be achieved by using launch mode as SingleTask of the first screen(Fragment A) if it is an Activity instead of Fragment.
My navigation graph XML file
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/app_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="FragmentA"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_FragmentA_to_FragmentB"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentB"
android:name="FragmentB"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_FragmentB_to_FragmentC"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentC"
android:name="FragmentC"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_fragemntC_to_fragementA"
app:launchSingleTop="true"
app:popUpToInclusive="true"
app:destination="#id/deliveriesFragment" />
</fragment>
</navigation>
But when the user press the back button from Fragment A it goes to Fragment C, the next back click goes to Fragment B, then the next back click to FRagment A.
If I understand correctly, your Fragment A is the start destination, like a main page, and you want the behavior of "whenever I come back to Fragment A, the next back button I click should exit the app", right?
If so, try to add action from any Fragment to Fragment A and set:
app:popUpTo="#id/fragmentA"
app:popUpToInclusive="true" />
It should look like this:
Demo: https://youtu.be/LNyk_FEkZoA
Posting my working solution, the full navigation script which includes the answer of #Sam Chen
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/app_navigation"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="FragmentA"
tools:layout="#layout/fragment_a">
<action
android:id="#+id/action_FragmentA_to_FragmentB"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentB"
android:name="FragmentB"
tools:layout="#layout/fragment_b">
<action
android:id="#+id/action_FragmentB_to_FragmentC"
app:destination="#id/dashboardFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_nav_graph"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentC"
android:name="FragmentC"
tools:layout="#layout/fragment_c">
<action
android:id="#+id/action_fragemntC_to_fragementA"
app:popUpToInclusive="true"
app:popUpTo="#id/fragmentA" />
</fragment>
</navigation>
I am trying to navigate from one navigation graph (with the host fragment on the LoginActivity) to another navigation graph (with the host fragment on the HomeActivity). I know Google is advocating for single activity applications and thus I am only going to be using these two Activities (to keep it minimal).
My login_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#id/nav_login">
<fragment
android:id="#+id/nav_login"
android:name="your.appname.LoginFragment"
android:label="#string/login_menu"
tools:layout="#layout/fragment_login" >
<action
android:id="#+id/action_nav_login_to_enterCellphone"
app:destination="#id/login_enter_cellphone"
app:popUpTo="#id/nav_login" />
</fragment>
<fragment
android:id="#+id/login_enter_cellphone"
android:name="your.appname.LoginEnterCellphoneFragment"
android:label="#string/enter_cellphone_fragment_label"
tools:layout="#layout/fragment_login_enter_cellphone" >
<action
android:id="#+id/action_enter_cellphone_to_enterOTPFragment"
app:destination="#id/enterOTPFragment"
app:popUpTo="#id/login_enter_cellphone" />
</fragment>
<fragment
android:id="#+id/enterOTPFragment"
android:name="your.appname.EnterOTPFragment"
android:label="#string/enter_otp_label"
tools:layout="#layout/fragment_enter_o_t_p" >
<action
android:id="#+id/action_enterOTPFragment_to_main_navigation"
app:destination="#id/main_navigation"
app:launchSingleTop="false" />
</fragment>
<include app:graph="#navigation/main_navigation" />
</navigation>
And main_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_navigation"
app:startDestination="#id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="your.appname.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/nav_profile"
android:name="your.appname.ProfileFragment"
android:label="#string/profile_menu"
tools:layout="#layout/fragment_profile" />
</navigation>
I found this answer and implemented it as follows
private fun navigateToHomeActivity() {
val action = EnterOTPFragmentDirections.actionEnterOTPFragmentToMainNavigation()
findNavController().navigate(action)
(activity as LoginActivity).finish() <-- later commented out
}
but it gives me the following error E/InputMethodManager: prepareNavigationBarInfo() rootView is null
If I comment out the above line the error goes away but it only replaces the current fragment with the HomeFragment.
How do I navigate to another Activity so that the source Activity is removed (Can't navigate back to it) and the Destination Activity is used?
You can define 2 navigation graphs:
ActivityA with navigation Graph A
ActivityB with navigation Graph B
In the GraphA you can define a destination to start ActivityB
<!-- Graph A -->
<navigation
....
<activity
android:id="#+id/activityB"
android:name=".ActivityB"
...>
<argument
.... />
</activity>
</navigation>
Then you can navigate to that activity ( = startActivity(intent)) using it like a destination:
Navigation.findNavController(view).navigate(R.id.activityB);
More info here.