Skip to content

Commit 21fca6c

Browse files
Merge branch 'main' into update-migration-snippet
2 parents fa74c7f + cd2e0c1 commit 21fca6c

File tree

8 files changed

+197
-1
lines changed

8 files changed

+197
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
17+
package com.example.compose.snippets.components
18+
19+
import androidx.compose.foundation.interaction.MutableInteractionSource
20+
import androidx.compose.foundation.interaction.collectIsPressedAsState
21+
import androidx.compose.foundation.layout.Row
22+
import androidx.compose.foundation.layout.Spacer
23+
import androidx.compose.foundation.layout.fillMaxWidth
24+
import androidx.compose.material3.Icon
25+
import androidx.compose.material3.IconButton
26+
import androidx.compose.material3.Text
27+
import androidx.compose.runtime.Composable
28+
import androidx.compose.runtime.LaunchedEffect
29+
import androidx.compose.runtime.getValue
30+
import androidx.compose.runtime.mutableIntStateOf
31+
import androidx.compose.runtime.mutableStateOf
32+
import androidx.compose.runtime.remember
33+
import androidx.compose.runtime.rememberUpdatedState
34+
import androidx.compose.runtime.saveable.rememberSaveable
35+
import androidx.compose.runtime.setValue
36+
import androidx.compose.ui.Alignment
37+
import androidx.compose.ui.Modifier
38+
import androidx.compose.ui.res.painterResource
39+
import androidx.compose.ui.tooling.preview.Preview
40+
import com.example.compose.snippets.R
41+
import kotlinx.coroutines.delay
42+
43+
// [START android_compose_components_togglebuttonexample]
44+
@Preview
45+
@Composable
46+
fun ToggleIconButtonExample() {
47+
// isToggled initial value should be read from a view model or persistent storage.
48+
var isToggled by rememberSaveable { mutableStateOf(false) }
49+
50+
IconButton(
51+
onClick = { isToggled = !isToggled }
52+
) {
53+
Icon(
54+
painter = if (isToggled) painterResource(R.drawable.favorite_filled) else painterResource(R.drawable.favorite),
55+
contentDescription = if (isToggled) "Selected icon button" else "Unselected icon button."
56+
)
57+
}
58+
}
59+
// [END android_compose_components_togglebuttonexample]
60+
61+
// [START android_compose_components_iconbutton]
62+
@Composable
63+
fun MomentaryIconButton(
64+
unselectedImage: Int,
65+
selectedImage: Int,
66+
contentDescription: String,
67+
modifier: Modifier = Modifier,
68+
stepDelay: Long = 100L, // Minimum value is 1L milliseconds.
69+
onClick: () -> Unit
70+
) {
71+
val interactionSource = remember { MutableInteractionSource() }
72+
val isPressed by interactionSource.collectIsPressedAsState()
73+
val pressedListener by rememberUpdatedState(onClick)
74+
75+
LaunchedEffect(isPressed) {
76+
while (isPressed) {
77+
delay(stepDelay.coerceIn(1L, Long.MAX_VALUE))
78+
pressedListener()
79+
}
80+
}
81+
82+
IconButton(
83+
modifier = modifier,
84+
onClick = onClick,
85+
interactionSource = interactionSource
86+
) {
87+
Icon(
88+
painter = if (isPressed) painterResource(id = selectedImage) else painterResource(id = unselectedImage),
89+
contentDescription = contentDescription,
90+
)
91+
}
92+
}
93+
// [END android_compose_components_iconbutton]
94+
95+
// [START android_compose_components_momentaryiconbuttons]
96+
@Preview()
97+
@Composable
98+
fun MomentaryIconButtonExample() {
99+
var pressedCount by remember { mutableIntStateOf(0) }
100+
101+
Row(
102+
modifier = Modifier.fillMaxWidth(),
103+
verticalAlignment = Alignment.CenterVertically
104+
) {
105+
MomentaryIconButton(
106+
unselectedImage = R.drawable.fast_rewind,
107+
selectedImage = R.drawable.fast_rewind_filled,
108+
stepDelay = 100L,
109+
onClick = { pressedCount -= 1 },
110+
contentDescription = "Decrease count button"
111+
)
112+
Spacer(modifier = Modifier)
113+
Text("advanced by $pressedCount frames")
114+
Spacer(modifier = Modifier)
115+
MomentaryIconButton(
116+
unselectedImage = R.drawable.fast_forward,
117+
selectedImage = R.drawable.fast_forward_filled,
118+
contentDescription = "Increase count button",
119+
stepDelay = 100L,
120+
onClick = { pressedCount += 1 }
121+
)
122+
}
123+
}
124+
// [END android_compose_components_momentaryiconbuttons]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="M100,720v-480l360,240 -360,240ZM500,720v-480l360,240 -360,240ZM180,480ZM580,480ZM180,570 L316,480 180,390v180ZM580,570 L716,480 580,390v180Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="M100,720v-480l360,240 -360,240ZM500,720v-480l360,240 -360,240Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="M860,720 L500,480l360,-240v480ZM460,720L100,480l360,-240v480ZM380,480ZM780,480ZM380,570v-180l-136,90 136,90ZM780,570v-180l-136,90 136,90Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="M860,720 L500,480l360,-240v480ZM460,720L100,480l360,-240v480Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="m480,840 l-58,-52q-101,-91 -167,-157T150,512.5Q111,460 95.5,416T80,326q0,-94 63,-157t157,-63q52,0 99,22t81,62q34,-40 81,-62t99,-22q94,0 157,63t63,157q0,46 -15.5,90T810,512.5Q771,565 705,631T538,788l-58,52ZM480,732q96,-86 158,-147.5t98,-107q36,-45.5 50,-81t14,-70.5q0,-60 -40,-100t-100,-40q-47,0 -87,26.5T518,280h-76q-15,-41 -55,-67.5T300,186q-60,0 -100,40t-40,100q0,35 14,70.5t50,81q36,45.5 98,107T480,732ZM480,459Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="960"
5+
android:viewportHeight="960">
6+
<path
7+
android:pathData="m480,840 l-58,-52q-101,-91 -167,-157T150,512.5Q111,460 95.5,416T80,326q0,-94 63,-157t157,-63q52,0 99,22t81,62q34,-40 81,-62t99,-22q94,0 157,63t63,157q0,46 -15.5,90T810,512.5Q771,565 705,631T538,788l-58,52Z"
8+
android:fillColor="#e3e3e3"/>
9+
</vector>

xr/src/main/java/com/example/xr/scenecore/Entities.kt

+19-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import androidx.xr.scenecore.MovableComponent
2929
import androidx.xr.scenecore.PlaneSemantic
3030
import androidx.xr.scenecore.PlaneType
3131
import androidx.xr.scenecore.ResizableComponent
32+
import androidx.xr.scenecore.ResizeListener
33+
import androidx.xr.scenecore.SurfaceEntity
34+
import java.util.concurrent.Executor
3235
import java.util.concurrent.Executors
3336

3437
private fun setPoseExample(entity: Entity) {
@@ -73,11 +76,26 @@ private fun moveableComponentExample(session: Session, entity: Entity) {
7376
// [END androidxr_scenecore_moveableComponentExample]
7477
}
7578

76-
private fun resizableComponentExample(session: Session, entity: Entity) {
79+
private fun resizableComponentExample(session: Session, entity: Entity, executor: Executor) {
7780
// [START androidxr_scenecore_resizableComponentExample]
7881
val resizableComponent = ResizableComponent.create(session)
7982
resizableComponent.minimumSize = Dimensions(177f, 100f, 1f)
8083
resizableComponent.fixedAspectRatio = 16f / 9f // Specify a 16:9 aspect ratio
84+
85+
resizableComponent.addResizeListener(
86+
executor,
87+
object : ResizeListener {
88+
override fun onResizeEnd(entity: Entity, finalSize: Dimensions) {
89+
90+
// update the size in the component
91+
resizableComponent.size = finalSize
92+
93+
// update the Entity to reflect the new size
94+
(entity as SurfaceEntity).canvasShape = SurfaceEntity.CanvasShape.Quad(finalSize.width, finalSize.height)
95+
}
96+
},
97+
)
98+
8199
entity.addComponent(resizableComponent)
82100
// [END androidxr_scenecore_resizableComponentExample]
83101
}

0 commit comments

Comments
 (0)