From c1abb57cd24354e2d4059e57c76dd478a3db61a8 Mon Sep 17 00:00:00 2001 From: calebvetter <39844454+calebvetter@users.noreply.github.com> Date: Tue, 11 Aug 2026 07:59:20 -0700 Subject: [PATCH] Add back stack for deep link navigation (#1756) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description When testing the new deep link feature from this PR [#1688](https://github.com/damontecres/Wholphin/pull/1688) I noticed that the back button would close the app. As in, all routes are cleared and only the playback is in the nav stack. I figured a more desirable behavior would be to create a nav stack, always starting with the home screen, then also inserting the media item's details page, if available. To achieve this, I made the return Target.destinations a list of destinations, and updated the other two uses in IntentService. Open to implementation tweaks, or scrap it if this isn't how it should work, haha. ### Testing Tested deep links, which produced the following nav stacks: - Movie view - Home → Movie details - Movie playback (with and without position) - Home → Movie details → Playback - Series view - Home → Series details - Season view - Home → Season details - Episode view - Home → Episode details - Episode playback (with and without position) - Home → Episode details → Playback - Trailer/extras playback (no details page available) - Home → Playback - TV channel view - Home → Playback - TV channel playback - Home → Playback ## AI or LLM usage AI was used to generate the initial code, debug and test, then manual refactoring and cleanup was done. --------- Co-authored-by: Damontecres --- .../damontecres/wholphin/MainActivity.kt | 9 +++- .../wholphin/services/IntentService.kt | 52 ++++++++++++++----- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index f1eb3b24..73ad2cbc 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -449,7 +449,14 @@ class MainActivityViewModel val current = serverRepository.current.value if (current != null) { Timber.i("Received valid intent, switching to AppContent") - navigationManager.replace(result.destination) + + if (result.addHomeToBackStack) { + navigationManager.reloadHome() + } else { + navigationManager.backStack.clear() + } + navigationManager.backStack.addAll(result.destinations) + setupNavigationManager.navigateTo( SetupDestination.AppContent(current), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/IntentService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/IntentService.kt index 0a043506..da59b98d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/IntentService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/IntentService.kt @@ -27,12 +27,11 @@ class IntentService Timber.v("Parsing intent %s", intent) Timber.v("Intent extras: %s", intent.extras) Timber.v("Intent data: %s", intent.data) - if (intent.getStringParam("type") != null) { - return getDestinationFromChannel(intent)?.let { IntentResult.Target(it) } - ?: IntentResult.Error("Invalid parameters") - } val action = intent.action ?: intent.data?.host - if (action == Intent.ACTION_MAIN || action.isNullOrBlank()) { + if ( + intent.getStringParam("type") == null && + (action == Intent.ACTION_MAIN || action.isNullOrBlank()) + ) { return IntentResult.NoOp } @@ -65,9 +64,19 @@ class IntentService } } + if (intent.getStringParam("type") != null) { + return getDestinationFromChannel(intent)?.let { + IntentResult.Target( + destinations = listOf(it), + addHomeToBackStack = false, + ) + } + ?: IntentResult.Error("Invalid parameters") + } + if (action == Intent.ACTION_SEARCH || action == "search") { val query = intent.getStringParam(SearchManager.QUERY) - return IntentResult.Target(Destination.Search(query ?: "")) + return IntentResult.Target(listOf(Destination.Search(query ?: ""))) } val itemId = intent.getStringParam("itemId")?.toUUIDOrNull() @@ -84,25 +93,39 @@ class IntentService return IntentResult.Error("Could not fetch item $itemId") } - val destination = + val itemDestination = item.destination() + + val destinations = when (action) { Intent.ACTION_VIEW, "view" -> { - item.destination() + listOf(itemDestination) } "com.github.damontecres.wholphin.PLAYBACK", "play" -> { val position = intent.getLongParam("position")?.coerceAtLeast(0) - Destination.Playback( - itemId = itemId, - positionMs = position ?: 0L, - ) + + val playbackDestination = + Destination.Playback( + itemId = itemId, + positionMs = position ?: 0L, + ) + + if (itemDestination is Destination.Playback) { + listOf(playbackDestination) + } else { + listOf( + itemDestination, + playbackDestination, + ) + } } else -> { return IntentResult.Error("Invalid action: ${intent.action}") } } - return IntentResult.Target(destination) + + return IntentResult.Target(destinations) } private fun Intent.getStringParam(key: String) = getStringExtra(key) ?: data?.getQueryParameter(key) @@ -159,6 +182,7 @@ sealed interface IntentResult { data object NoOp : IntentResult data class Target( - val destination: Destination, + val destinations: List, + val addHomeToBackStack: Boolean = true, ) : IntentResult }