Using DeepLinks in Compose leads to inability to navigate backwards
Asked Answered
R

1

7

I have a FirebaseMessagingService subclass that receives a notification payload and then creates a push notification with the following PendingIntent:

        TaskStackBuilder.create(this).run {
            addNextIntentWithParentStack(
                Intent(
                    Intent.ACTION_VIEW,
                    "eway://traversals_monthly_bill_details".toUri()
                )
            )
            getPendingIntent(2345, PendingIntent.FLAG_UPDATE_CURRENT)
        }

Here are the composables of the respective nested graph of our navigation:

    composable(Screens.INTERMEDIATE_MONTHLY_BILLINGS.navRoute,
        deepLinks =
        listOf(
            navDeepLink {
                uriPattern =
                    "eway://traversals_monthly_bill_details"
            }
        )) {
        IntermediateMonthlyBillings({ statements ->
            val lastIndex = statements.lastIndex
            val gson = GsonBuilder().create()
            val statementsByYearJson = gson.toJson(statements)
            navController.navigate("${Screens.MONTHLY_BILLINGS}/$statementsByYearJson/$lastIndex")
        })
    }
    composable("${Screens.MONTHLY_BILLINGS.navRoute}/{statementsByYear}/{initialIndex}",
        arguments = listOf(
            navArgument("statementsByYear") {},
            navArgument("initialIndex") {
                type = NavType.IntType
            }
        )
    ) {
        val gson = GsonBuilder().create()
        val statementsByYear = gson.fromJson(
            it.arguments?.getString("statementsByYear"),
            AllStatementsByYear::class.java
        )
        MonthlyBillings(statementsByYear, it.arguments?.getInt("initialIndex")!!)
        updateCurrentScreen(Screens.MONTHLY_BILLINGS)
    }

Here's the Intermediate screen:

@Composable
fun IntermediateMonthlyBillings(
    onDataAcquired: (AllStatementsByYear) -> Unit,
    myEwayLoggedInViewModel: MyEwayLoggedInViewModel = get()
) {
    val statementsByYear by myEwayLoggedInViewModel.statementsByYear.observeAsState(null)

    if (statementsByYear == null) {
        GenericLoader(type = MyLoaderType.LIGHT_BACKGROUND)
    }
    LaunchedEffect(statementsByYear == null) {
        if (statementsByYear != null) {
            onDataAcquired(statementsByYear!!)
        }
    }
}

The issue is, that although the deep-link works and I am navigated to the intermediate screen and have the callback onDataAcquired run after I've received my data (which then navigates me to the MonthlyBillings screen), it looks as if the backstack is full of instances of either the MonthlyBillings or the Intermediate screens and I can't tell why.

After I've reached the deeplink's destination, I need to spam the back button and after ~10 attempts it will start navigating me back the original stack.

What I actually want is for the stack to only have the final destination screen (MonthlyBillings) along with the series of backstack entries that it would have if the user was navigating manually (for example ScreenA, ScreenC, ScreenF, MonthlyBillings).

Rhizomorphous answered 1/7, 2022 at 7:59 Comment(0)
R
5

Alright after logging the backQueue destination routes inside the NavGraph's Intermediate composable destination like so:

 composable(Screens.INTERMEDIATE_MONTHLY_BILLINGS.navRoute,
        deepLinks =
        listOf(
            navDeepLink {
                uriPattern =
                    "eway://traversals_monthly_bill_details"
            }
        )) {
        Log.d("PAYMENTSGRAPH", "composable called with backstack ${navController.backQueue}")
        navController.backQueue.forEach {
            Log.d("PAYMENTSGRAPH", it.destination.route.toString())
        }
        IntermediateMonthlyBillings({ statements ->
            Log.d("CALLBACK_PAYMENTSGRAPH", "callback called")
            val lastIndex = statements.lastIndex
            val gson = GsonBuilder().create()
            val statementsByYearJson = gson.toJson(statements)navController.navigate("${Screens.MONTHLY_BILLINGS}/$statementsByYearJson/$lastIndex")
        })
    }

I noticed the following output whenever I was opening the deep-link:

2022-07-05 11:24:27.802 16304-16304/[REDACTED] D/PAYMENTSGRAPH: null 16304-16304/[REDACTED] D/PAYMENTSGRAPH: splash_screen_graph 16304-16304/[REDACTED] D/PAYMENTSGRAPH: splash_screen 16304-16304/[REDACTED] D/PAYMENTSGRAPH: payments_graph 16304-16304/[REDACTED] D/PAYMENTSGRAPH: payments_traversals D/PAYMENTSGRAPH: intermediate_monthly_billings 16304-16304/[REDACTED] D/PAYMENTSGRAPH: monthly_billings/{statementsByYear}/{initialIndex}

Then I realized that since the D/PAYMENTSGRAPH: intermediate_monthly_billings destination was still in the stack, after navigating back from the monthly_billings/{statementsByYear}/{initialIndex} destination, the logic within the intermediate screen would run again, so it was recreating the NavBackStackEntry for the monthly_billings screen and navigating me back there. If I spammed the back button fast enough, I could "outrun" the recreation hence why it could sometimes "work".

The fix is a simple navController.popBackStack() before navigating to the monthly_billings screen from within the callback. Here's the final code:

    composable(Screens.INTERMEDIATE_MONTHLY_BILLINGS.navRoute,
        deepLinks =
        listOf(
            navDeepLink {
                uriPattern =
                    "eway://traversals_monthly_bill_details"
            }
        )) {
        IntermediateMonthlyBillings({ statements ->
            val lastIndex = statements.lastIndex
            val gson = GsonBuilder().create()
            val statementsByYearJson = gson.toJson(statements)
            navController.popBackStack()
            navController.navigate("${Screens.MONTHLY_BILLINGS}/$statementsByYearJson/$lastIndex")
        })
    }
Rhizomorphous answered 5/7, 2022 at 8:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.