Skip to content

Commit 5fd5daf

Browse files
committed
Add Spatial layout for CustomizeExportScreen
1 parent d56de3a commit 5fd5daf

File tree

3 files changed

+176
-38
lines changed

3 files changed

+176
-38
lines changed

feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportLayoutType.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@
1616
package com.android.developers.androidify.customize
1717

1818
import androidx.compose.runtime.Composable
19+
import androidx.xr.compose.platform.LocalSpatialCapabilities
1920
import com.android.developers.androidify.util.isAtLeastMedium
2021

2122
enum class CustomizeExportLayoutType {
2223
Compact,
2324
Medium,
25+
Spatial,
2426
}
2527

2628
@Composable
27-
fun calculateLayoutType(): CustomizeExportLayoutType {
29+
fun calculateLayoutType(enableXr: Boolean = false): CustomizeExportLayoutType {
2830
return when {
31+
LocalSpatialCapabilities.current.isSpatialUiEnabled && enableXr -> CustomizeExportLayoutType.Spatial
2932
isAtLeastMedium() -> CustomizeExportLayoutType.Medium
3033
else -> CustomizeExportLayoutType.Compact
3134
}

feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportScreen.kt

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,18 @@ import androidx.compose.ui.draw.dropShadow
6363
import androidx.compose.ui.graphics.Color
6464
import androidx.compose.ui.graphics.shadow.Shadow
6565
import androidx.compose.ui.graphics.vector.ImageVector
66-
import androidx.compose.ui.layout.LookaheadScope
6766
import androidx.compose.ui.platform.LocalContext
6867
import androidx.compose.ui.res.stringResource
6968
import androidx.compose.ui.res.vectorResource
7069
import androidx.compose.ui.tooling.preview.Preview
7170
import androidx.compose.ui.unit.dp
7271
import androidx.lifecycle.compose.collectAsStateWithLifecycle
7372
import androidx.navigation3.ui.LocalNavAnimatedContentScope
73+
import com.android.developers.androidify.customize.xr.CustomizeExportLayoutSpatial
7474
import com.android.developers.androidify.results.PermissionRationaleDialog
7575
import com.android.developers.androidify.results.R
7676
import com.android.developers.androidify.results.shareImage
7777
import com.android.developers.androidify.theme.AndroidifyTheme
78-
import com.android.developers.androidify.theme.LocalAnimateBoundsScope
7978
import com.android.developers.androidify.theme.components.AboutButton
8079
import com.android.developers.androidify.theme.components.AndroidifyTopAppBar
8180
import com.android.developers.androidify.theme.components.PrimaryButton
@@ -86,6 +85,10 @@ import com.android.developers.androidify.util.PhonePreview
8685
import com.android.developers.androidify.util.allowsFullContent
8786
import com.android.developers.androidify.watchface.WatchFaceAsset
8887
import com.android.developers.androidify.wear.common.ConnectedWatch
88+
import com.android.developers.androidify.xr.RequestFullSpaceIconButton
89+
import com.android.developers.androidify.xr.RequestHomeSpaceIconButton
90+
import com.android.developers.androidify.xr.couldRequestFullSpace
91+
import com.android.developers.androidify.xr.couldRequestHomeSpace
8992
import com.google.accompanist.permissions.ExperimentalPermissionsApi
9093
import com.google.accompanist.permissions.isGranted
9194
import com.google.accompanist.permissions.rememberPermissionState
@@ -110,7 +113,7 @@ fun CustomizeAndExportScreen(
110113
}
111114
}
112115

113-
val layoutType = calculateLayoutType()
116+
val layoutType = calculateLayoutType(enableXr = state.value.xrEnabled)
114117
CustomizeExportContents(
115118
state.value,
116119
onBackPress,
@@ -193,10 +196,18 @@ private fun CustomizeExportContents(
193196
AndroidifyTopAppBar(
194197
backEnabled = true,
195198
titleText = stringResource(R.string.customize_and_export),
196-
isMediumWindowSize = layoutType == CustomizeExportLayoutType.Medium,
199+
isMediumWindowSize = layoutType != CustomizeExportLayoutType.Compact,
197200
onBackPressed = onBackPress,
198201
actions = {
199202
AboutButton { onInfoPress() }
203+
if (state.xrEnabled) {
204+
if (couldRequestFullSpace()) {
205+
RequestFullSpaceIconButton()
206+
}
207+
if (couldRequestHomeSpace()) {
208+
RequestHomeSpaceIconButton()
209+
}
210+
}
200211
},
201212
)
202213
}
@@ -255,40 +266,46 @@ private fun CustomizeExportContents(
255266
)
256267
}
257268
}
258-
LookaheadScope {
259-
CompositionLocalProvider(LocalAnimateBoundsScope provides this) {
260-
when (layoutType) {
261-
CustomizeExportLayoutType.Medium -> CustomizeExportScreenScaffold(
262-
snackbarHostState,
263-
topBar = topBar,
264-
containerColor = MaterialTheme.colorScheme.surface,
265-
) { paddingValues ->
266-
CustomizeExportLayoutMedium(
267-
paddingValues = paddingValues,
268-
imageResult = imageResult,
269-
state = state,
270-
toolDetail = toolDetail,
271-
toolSelector = toolSelector,
272-
actionButtons = actionButtons,
273-
)
274-
}
269+
when (layoutType) {
270+
CustomizeExportLayoutType.Medium -> CustomizeExportScreenScaffold(
271+
snackbarHostState,
272+
topBar = topBar,
273+
containerColor = MaterialTheme.colorScheme.surface,
274+
) { paddingValues ->
275+
CustomizeExportLayoutMedium(
276+
paddingValues = paddingValues,
277+
imageResult = imageResult,
278+
state = state,
279+
toolDetail = toolDetail,
280+
toolSelector = toolSelector,
281+
actionButtons = actionButtons,
282+
)
283+
}
275284

276-
else -> CustomizeExportScreenScaffold(
277-
snackbarHostState,
278-
topBar = topBar,
279-
containerColor = MaterialTheme.colorScheme.surface,
280-
) { paddingValues ->
281-
CustomizeExportLayoutCompact(
282-
paddingValues = paddingValues,
283-
imageResult = imageResult,
284-
state = state,
285-
toolSelector = toolSelector,
286-
toolDetail = toolDetail,
287-
actionButtons = actionButtons,
288-
)
289-
}
290-
}
285+
CustomizeExportLayoutType.Compact -> CustomizeExportScreenScaffold(
286+
snackbarHostState,
287+
topBar = topBar,
288+
containerColor = MaterialTheme.colorScheme.surface,
289+
) { paddingValues ->
290+
CustomizeExportLayoutCompact(
291+
paddingValues = paddingValues,
292+
imageResult = imageResult,
293+
state = state,
294+
toolSelector = toolSelector,
295+
toolDetail = toolDetail,
296+
actionButtons = actionButtons,
297+
)
291298
}
299+
300+
CustomizeExportLayoutType.Spatial -> CustomizeExportLayoutSpatial(
301+
imageResult = imageResult,
302+
state = state,
303+
toolSelector = toolSelector,
304+
toolDetail = toolDetail,
305+
actionButtons = actionButtons,
306+
snackbarHostState = snackbarHostState,
307+
topBar = topBar,
308+
)
292309
}
293310
}
294311

@@ -533,7 +550,7 @@ fun CustomizeExportScreenScaffold(
533550
},
534551
topBar = topBar,
535552
containerColor = containerColor,
536-
modifier = modifier.fillMaxSize(),
553+
modifier = modifier,
537554
content = content,
538555
)
539556
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.android.developers.androidify.customize.xr
17+
18+
import androidx.compose.foundation.background
19+
import androidx.compose.foundation.layout.Box
20+
import androidx.compose.foundation.layout.Column
21+
import androidx.compose.foundation.layout.Spacer
22+
import androidx.compose.foundation.layout.fillMaxSize
23+
import androidx.compose.foundation.layout.size
24+
import androidx.compose.material3.MaterialTheme
25+
import androidx.compose.material3.SnackbarHostState
26+
import androidx.compose.runtime.Composable
27+
import androidx.compose.ui.Alignment
28+
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.unit.dp
31+
import androidx.xr.compose.spatial.ContentEdge
32+
import androidx.xr.compose.spatial.Orbiter
33+
import androidx.xr.compose.spatial.OrbiterOffsetType
34+
import androidx.xr.compose.subspace.SpatialBox
35+
import androidx.xr.compose.subspace.SpatialColumn
36+
import androidx.xr.compose.subspace.SpatialLayoutSpacer
37+
import androidx.xr.compose.subspace.SpatialPanel
38+
import androidx.xr.compose.subspace.SpatialRow
39+
import androidx.xr.compose.subspace.layout.SubspaceModifier
40+
import androidx.xr.compose.subspace.layout.fillMaxHeight
41+
import androidx.xr.compose.subspace.layout.fillMaxWidth
42+
import androidx.xr.compose.subspace.layout.offset
43+
import androidx.xr.compose.subspace.layout.width
44+
import com.android.developers.androidify.customize.CustomizeExportScreenScaffold
45+
import com.android.developers.androidify.customize.CustomizeExportState
46+
import com.android.developers.androidify.customize.ExportImageCanvas
47+
import com.android.developers.androidify.xr.DisableSharedTransition
48+
import com.android.developers.androidify.xr.MainPanelWorkaround
49+
import com.android.developers.androidify.xr.SquiggleBackgroundSubspace
50+
51+
@Composable
52+
fun CustomizeExportLayoutSpatial(
53+
state: CustomizeExportState,
54+
snackbarHostState: SnackbarHostState,
55+
imageResult: @Composable (ExportImageCanvas.() -> Unit),
56+
toolDetail: @Composable (Modifier, Boolean) -> Unit,
57+
toolSelector: @Composable (Modifier, Boolean) -> Unit,
58+
actionButtons: @Composable (Modifier) -> Unit,
59+
topBar: @Composable () -> Unit,
60+
) {
61+
DisableSharedTransition {
62+
SquiggleBackgroundSubspace(minimumHeight = 600.dp) {
63+
MainPanelWorkaround()
64+
SpatialColumn(SubspaceModifier.fillMaxWidth()) {
65+
Orbiter(position = ContentEdge.Bottom, alignment = Alignment.End) {
66+
actionButtons(Modifier)
67+
}
68+
SpatialPanel(
69+
SubspaceModifier.offset(z = 10.dp)
70+
.fillMaxWidth(0.5f),
71+
) {
72+
Column(
73+
Modifier.background(
74+
color = MaterialTheme.colorScheme.surfaceContainerLowest,
75+
shape = MaterialTheme.shapes.large,
76+
),
77+
) {
78+
topBar()
79+
Spacer(Modifier.size(16.dp))
80+
}
81+
}
82+
83+
SpatialRow(SubspaceModifier.fillMaxWidth(0.7f)) {
84+
SpatialPanel(
85+
modifier = SubspaceModifier
86+
.offset(z = 10.dp)
87+
.weight(1f, fill = true)
88+
.fillMaxHeight(0.8f),
89+
) {
90+
CustomizeExportScreenScaffold(
91+
snackbarHostState = snackbarHostState,
92+
topBar = {},
93+
containerColor = Color.Transparent,
94+
) {
95+
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
96+
imageResult(state.exportImageCanvas)
97+
}
98+
}
99+
}
100+
SpatialLayoutSpacer(SubspaceModifier.width(48.dp))
101+
SpatialBox(SubspaceModifier.fillMaxHeight(0.7f)) {
102+
SpatialPanel(
103+
modifier = SubspaceModifier.offset(z = 10.dp).fillMaxWidth(0.3f),
104+
) {
105+
toolDetail(Modifier, false)
106+
Orbiter(
107+
position = ContentEdge.End,
108+
offsetType = OrbiterOffsetType.InnerEdge,
109+
) {
110+
toolSelector(Modifier, false)
111+
}
112+
}
113+
}
114+
}
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)