Skip to content
This repository was archived by the owner on Jun 11, 2026. It is now read-only.

Commit 7478b6f

Browse files
authored
Add argument support for TwoPaneLayoutNav destinations (#60)
* Update release versions in main readme * Update dependencies * Bump version * Store graphContent with destination keys instead of strings * Make sure default arg values are used * Suppress restricted api warnings
1 parent 40e6802 commit 7478b6f

7 files changed

Lines changed: 66 additions & 36 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ A utility component that helps you easily get details about the window state of
3838

3939
Helper functions that help you easily test your application on the dual-screen, foldable and large screen devices.
4040

41-
#### Latest Update: [1.0.0-alpha08](https://github.com/microsoft/surface-duo-compose-sdk/releases/tag/composetesting_20230208_alpha08) (February 8th, 2023)
41+
#### Latest Update: [1.0.0-alpha09](https://github.com/microsoft/surface-duo-compose-sdk/releases/tag/composetesting_20230505_alpha09) (May 5th, 2023)
4242

4343
### [DragAndDrop](https://github.com/microsoft/surface-duo-compose-sdk/tree/main/DragAndDrop)
4444

4545
A Compose component that helps you build drag and drop feature on any Android devices, especially dual-screen, foldable and large-screen devices.
4646

47-
#### Latest Update: [1.0.0-alpha04](https://github.com/microsoft/surface-duo-compose-sdk/releases/tag/draganddrop_20230208_alpha04) (February 8th, 2023)
47+
#### Latest Update: [1.0.0-alpha05](https://github.com/microsoft/surface-duo-compose-sdk/releases/tag/draganddrop_20230505_alpha05) (May 5th, 2023)
4848

4949
## Social links
5050

TwoPaneLayout/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ When the app is spanned across a separating vertical hinge or fold, or when the
2020
2. Add dependencies to the module-level **build.gradle** file (current version may be different from what's shown here).
2121
2222
```gradle
23-
implementation "com.microsoft.device.dualscreen:twopanelayout:1.0.1-alpha06"
23+
implementation "com.microsoft.device.dualscreen:twopanelayout:1.0.1-alpha07"
2424
```
2525
2626
3. Also ensure the compileSdkVersion is set to API 33 and the targetSdkVersion is set to API 32 or newer in the module-level **build.gradle** file.

TwoPaneLayout/dependencies.gradle

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ ext {
1414

1515
// TwoPaneLayout library version code:
1616
// If you want to publish a new version, bump in one (1) the specific line(s)
17-
twoPaneLayoutVersionCode = 22
17+
twoPaneLayoutVersionCode = 23
1818

1919
// TwoPaneLayout library version name:
2020
// If you want to publish a new version, bump the specific line
21-
twoPaneLayoutVersionName = '1.0.1-alpha06'
21+
twoPaneLayoutVersionName = '1.0.1-alpha07'
2222

2323
// ----------------------------------
2424

25-
gradlePluginVersion = '7.3.0'
26-
kotlinVersion = '1.8.0'
25+
gradlePluginVersion = '8.0.0'
26+
kotlinVersion = '1.8.21'
2727
compileSdkVersion = 33
2828
targetSdkVersion = 32
2929
minSdkVersion = 24
@@ -44,18 +44,18 @@ ext {
4444
]
4545

4646
// AndroidX dependencies
47-
appCompatVersion = '1.6.0'
48-
ktxCoreVersion = '1.9.0'
47+
appCompatVersion = '1.6.1'
48+
ktxCoreVersion = '1.10.0'
4949
androidxDependencies = [
5050
appCompat : "androidx.appcompat:appcompat:$appCompatVersion",
5151
ktxCore : "androidx.core:core-ktx:$ktxCoreVersion",
5252
]
5353

5454
// Compose dependencies
55-
composeVersion = '1.3.3'
56-
composeMaterialVersion = '1.3.1'
57-
composeCompilerVersion = '1.4.0'
58-
activityComposeVersion = '1.6.1'
55+
composeVersion = '1.4.3'
56+
composeMaterialVersion = '1.4.3'
57+
composeCompilerVersion = '1.4.7'
58+
activityComposeVersion = '1.7.1'
5959
navigationComposeVersion = '2.5.3'
6060
composeDependencies = [
6161
composeMaterial : "androidx.compose.material:material:$composeMaterialVersion",
@@ -84,8 +84,8 @@ ext {
8484
]
8585

8686
// Microsoft dependencies
87-
windowStateVersion = '1.0.0-alpha08'
88-
composeTestingVersion = '1.0.0-alpha07'
87+
windowStateVersion = '1.0.0-alpha09'
88+
composeTestingVersion = '1.0.0-alpha09'
8989
microsoftDependencies = [
9090
windowState : "com.microsoft.device.dualscreen:windowstate:$windowStateVersion",
9191
composeTesting : "com.microsoft.device.dualscreen.testing:testing-compose:$composeTestingVersion",

TwoPaneLayout/gradle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ android.useAndroidX=true
1919
android.enableJetifier=true
2020
# Kotlin code style for this project: "official" or "obsolete":
2121
kotlin.code.style=official
22-
android.disableAutomaticComponentCreation=true
22+
android.defaults.buildfeatures.buildconfig=true
23+
android.nonTransitiveRClass=false
24+
android.nonFinalResIds=false
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Thu Apr 15 16:13:21 PDT 2021
22
distributionBase=GRADLE_USER_HOME
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
44
distributionPath=wrapper/dists
55
zipStorePath=wrapper/dists
66
zipStoreBase=GRADLE_USER_HOME

TwoPaneLayout/library/src/main/java/com/microsoft/device/dualscreen/twopanelayout/twopanelayoutnav/TwoPaneBackStackEntry.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ fun NavGraphBuilder.composable(
4545
deepLinks: List<NavDeepLink> = emptyList(),
4646
content: @Composable TwoPaneNavScope.(NavBackStackEntry) -> Unit
4747
) {
48-
graphContent[route] = content
49-
50-
addDestination(
48+
val destination =
5149
ComposeNavigator.Destination(provider[ComposeNavigator::class]) { TwoPaneNavScopeInstance.content(it) }
5250
.apply {
5351
this.route = route
@@ -58,5 +56,8 @@ fun NavGraphBuilder.composable(
5856
addDeepLink(deepLink)
5957
}
6058
}
61-
)
59+
60+
graphDestinations[destination] = content
61+
62+
addDestination(destination)
6263
}

TwoPaneLayout/library/src/main/java/com/microsoft/device/dualscreen/twopanelayout/twopanelayoutnav/TwoPaneLayoutNavCore.kt

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.microsoft.device.dualscreen.twopanelayout.twopanelayoutnav
22

33
import android.annotation.SuppressLint
4+
import android.content.Context
5+
import android.os.Bundle
46
import androidx.activity.compose.BackHandler
57
import androidx.compose.runtime.Composable
68
import androidx.compose.runtime.getValue
@@ -10,11 +12,15 @@ import androidx.compose.runtime.saveable.rememberSaveable
1012
import androidx.compose.runtime.setValue
1113
import androidx.compose.ui.Modifier
1214
import androidx.compose.ui.layout.Layout
15+
import androidx.compose.ui.platform.LocalContext
16+
import androidx.core.net.toUri
1317
import androidx.navigation.NavBackStackEntry
18+
import androidx.navigation.NavDeepLinkRequest
1419
import androidx.navigation.NavDestination
1520
import androidx.navigation.NavGraph
1621
import androidx.navigation.NavHostController
1722
import androidx.navigation.NavOptionsBuilder
23+
import androidx.navigation.compose.ComposeNavigator
1824
import androidx.navigation.compose.NavHost
1925
import com.microsoft.device.dualscreen.twopanelayout.Screen
2026
import com.microsoft.device.dualscreen.twopanelayout.TwoPaneNavScope
@@ -32,27 +38,47 @@ internal var getPane1Destination: () -> String = { "" }
3238
internal var getPane2Destination: () -> String = { "" }
3339
internal var getSinglePaneDestination: () -> String = { "" }
3440
internal val backStack = mutableListOf<TwoPaneBackStackEntry>()
35-
internal val graphContent = mutableMapOf<String, @Composable TwoPaneNavScope.(NavBackStackEntry) -> Unit>()
41+
internal val graphDestinations =
42+
mutableMapOf<ComposeNavigator.Destination, @Composable TwoPaneNavScope.(NavBackStackEntry) -> Unit>()
3643

3744
internal fun isSinglePaneHandler(): Boolean {
3845
return isSinglePane
3946
}
4047

41-
private fun verifyRoute(route: String) {
42-
if (!graphContent.keys.contains(route))
43-
throw IllegalArgumentException("Invalid route $route, not present in list of routes ${graphContent.keys}")
48+
@SuppressLint("RestrictedApi")
49+
private fun getDeepLinkMatchForRoute(route: String): NavDestination.DeepLinkMatch {
50+
val navDeepLinkRequest = NavDeepLinkRequest.Builder.fromUri(NavDestination.createRoute(route).toUri()).build()
51+
var match: NavDestination.DeepLinkMatch? = null
52+
53+
graphDestinations.keys.firstOrNull {
54+
match = it.matchDeepLink(navDeepLinkRequest)
55+
match != null
56+
}
57+
?: throw IllegalArgumentException("Invalid route $route, not present in list of destinations ${graphDestinations.keys}")
58+
59+
return match!!.apply { removePlaceholderArgs() }
4460
}
4561

46-
private fun findGraphContent(route: String): @Composable TwoPaneNavScope.() -> Unit {
47-
verifyRoute(route)
62+
@SuppressLint("RestrictedApi")
63+
private fun NavDestination.DeepLinkMatch.removePlaceholderArgs() {
64+
matchingArgs?.let {
65+
for (argName in it.keySet()) {
66+
if (it.getString(argName) == "{$argName}")
67+
matchingArgs?.remove(argName)
68+
}
69+
}
70+
}
4871

49-
val content = graphContent[route]!!
72+
@SuppressLint("RestrictedApi")
73+
private fun findGraphContent(route: String, context: Context): @Composable TwoPaneNavScope.() -> Unit {
74+
val match = getDeepLinkMatchForRoute(route)
75+
val destination = match.destination
76+
val content = graphDestinations[destination]!!
77+
val args = destination.addInDefaultArgs(match.matchingArgs) ?: Bundle()
5078

51-
// REVISIT: passing in empty entry, may update in the future if arguments need to be passed through
52-
@SuppressLint("RestrictedApi")
53-
val emptyBackStackEntry = NavBackStackEntry.create(null, NavDestination(""))
79+
val backStackEntry = NavBackStackEntry.create(context, destination, args)
5480

55-
return { content(emptyBackStackEntry) }
81+
return { content(backStackEntry) }
5682
}
5783

5884
@Composable
@@ -110,13 +136,13 @@ internal fun TwoPaneContainer(
110136
// Initialize navigation method handlers
111137
navigatePane1To = { route ->
112138
if (currentPane1 != route) {
113-
verifyRoute(route)
139+
getDeepLinkMatchForRoute(route)
114140
currentPane1 = route
115141
}
116142
}
117143
navigatePane2To = { route ->
118144
if (currentPane2 != route) {
119-
verifyRoute(route)
145+
getDeepLinkMatchForRoute(route)
120146
currentPane2 = route
121147
}
122148
}
@@ -126,8 +152,9 @@ internal fun TwoPaneContainer(
126152
backStack.initialize(pane1StartDestination, pane2StartDestination)
127153

128154
// Find the destinations to display in each pane
129-
val pane1 = findGraphContent(currentPane1)
130-
val pane2 = findGraphContent(currentPane2)
155+
val context = LocalContext.current
156+
val pane1 = findGraphContent(currentPane1, context)
157+
val pane2 = findGraphContent(currentPane2, context)
131158

132159
val measurePolicy = twoPaneMeasurePolicy(
133160
windowMode = windowState.windowMode,

0 commit comments

Comments
 (0)