Skip to content

Commit 60a0e71

Browse files
Merge branch 'main' into jv/swipe-dismiss-improvements
2 parents 4a0d74d + 015df95 commit 60a0e71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4155
-3
lines changed

.github/workflows/apply_spotless.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ jobs:
5050
- name: Run spotlessApply for Misc
5151
run: ./gradlew :misc:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
5252

53+
- name: Run spotlessApply for XR
54+
run: ./gradlew :xr:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
55+
5356
- name: Auto-commit if spotlessApply has changes
5457
uses: stefanzweifel/git-auto-commit-action@v5
5558
with:

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ jobs:
4747
run: ./gradlew :wear:build
4848
- name: Build misc snippets
4949
run: ./gradlew :misc:build
50+
- name: Build XR snippets
51+
run: ./gradlew :xr:build
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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.layout.Box
20+
import androidx.compose.foundation.layout.fillMaxSize
21+
import androidx.compose.foundation.layout.padding
22+
import androidx.compose.material.icons.Icons
23+
import androidx.compose.material.icons.filled.Album
24+
import androidx.compose.material.icons.filled.MusicNote
25+
import androidx.compose.material.icons.filled.PlaylistAddCircle
26+
import androidx.compose.material3.ExperimentalMaterial3Api
27+
import androidx.compose.material3.Icon
28+
import androidx.compose.material3.NavigationBar
29+
import androidx.compose.material3.NavigationBarDefaults
30+
import androidx.compose.material3.NavigationBarItem
31+
import androidx.compose.material3.NavigationRail
32+
import androidx.compose.material3.NavigationRailItem
33+
import androidx.compose.material3.PrimaryTabRow
34+
import androidx.compose.material3.Scaffold
35+
import androidx.compose.material3.Tab
36+
import androidx.compose.material3.Text
37+
import androidx.compose.runtime.Composable
38+
import androidx.compose.runtime.getValue
39+
import androidx.compose.runtime.mutableIntStateOf
40+
import androidx.compose.runtime.saveable.rememberSaveable
41+
import androidx.compose.runtime.setValue
42+
import androidx.compose.ui.Alignment
43+
import androidx.compose.ui.Modifier
44+
import androidx.compose.ui.graphics.vector.ImageVector
45+
import androidx.compose.ui.text.style.TextOverflow
46+
import androidx.compose.ui.tooling.preview.Preview
47+
import androidx.navigation.NavHostController
48+
import androidx.navigation.compose.NavHost
49+
import androidx.navigation.compose.composable
50+
import androidx.navigation.compose.rememberNavController
51+
52+
@Composable
53+
fun SongsScreen(modifier: Modifier = Modifier) {
54+
Box(
55+
modifier = Modifier.fillMaxSize(),
56+
contentAlignment = Alignment.Center
57+
) {
58+
Text("Songs Screen")
59+
}
60+
}
61+
62+
@Composable
63+
fun AlbumScreen(modifier: Modifier = Modifier) {
64+
Box(
65+
modifier = Modifier.fillMaxSize(),
66+
contentAlignment = Alignment.Center
67+
) {
68+
Text("Album Screen")
69+
}
70+
}
71+
72+
@Composable
73+
fun PlaylistScreen(modifier: Modifier = Modifier) {
74+
Box(
75+
modifier = Modifier.fillMaxSize(),
76+
contentAlignment = Alignment.Center
77+
) {
78+
Text("Playlist Screen")
79+
}
80+
}
81+
82+
enum class Destination(
83+
val route: String,
84+
val label: String,
85+
val icon: ImageVector,
86+
val contentDescription: String
87+
) {
88+
SONGS("songs", "Songs", Icons.Default.MusicNote, "Songs"),
89+
ALBUM("album", "Album", Icons.Default.Album, "Album"),
90+
PLAYLISTS("playlist", "Playlist", Icons.Default.PlaylistAddCircle, "Playlist")
91+
}
92+
93+
@Composable
94+
fun AppNavHost(
95+
navController: NavHostController,
96+
startDestination: Destination,
97+
modifier: Modifier = Modifier
98+
) {
99+
NavHost(
100+
navController,
101+
startDestination = startDestination.route
102+
) {
103+
Destination.entries.forEach { destination ->
104+
composable(destination.route) {
105+
when (destination) {
106+
Destination.SONGS -> SongsScreen()
107+
Destination.ALBUM -> AlbumScreen()
108+
Destination.PLAYLISTS -> PlaylistScreen()
109+
}
110+
}
111+
}
112+
}
113+
}
114+
115+
@Preview()
116+
// [START android_compose_components_navigationbarexample]
117+
@Composable
118+
fun NavigationBarExample(modifier: Modifier = Modifier) {
119+
val navController = rememberNavController()
120+
val startDestination = Destination.SONGS
121+
var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
122+
123+
Scaffold(
124+
modifier = modifier,
125+
bottomBar = {
126+
NavigationBar(windowInsets = NavigationBarDefaults.windowInsets) {
127+
Destination.entries.forEachIndexed { index, destination ->
128+
NavigationBarItem(
129+
selected = selectedDestination == index,
130+
onClick = {
131+
navController.navigate(route = destination.route)
132+
selectedDestination = index
133+
},
134+
icon = {
135+
Icon(
136+
destination.icon,
137+
contentDescription = destination.contentDescription
138+
)
139+
},
140+
label = { Text(destination.label) }
141+
)
142+
}
143+
}
144+
}
145+
) { contentPadding ->
146+
AppNavHost(navController, startDestination, modifier = Modifier.padding(contentPadding))
147+
}
148+
}
149+
// [END android_compose_components_navigationbarexample]
150+
151+
@Preview()
152+
// [START android_compose_components_navigationrailexample]
153+
@Composable
154+
fun NavigationRailExample(modifier: Modifier = Modifier) {
155+
val navController = rememberNavController()
156+
val startDestination = Destination.SONGS
157+
var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
158+
159+
Scaffold(modifier = modifier) { contentPadding ->
160+
NavigationRail(modifier = Modifier.padding(contentPadding)) {
161+
Destination.entries.forEachIndexed { index, destination ->
162+
NavigationRailItem(
163+
selected = selectedDestination == index,
164+
onClick = {
165+
navController.navigate(route = destination.route)
166+
selectedDestination = index
167+
},
168+
icon = {
169+
Icon(
170+
destination.icon,
171+
contentDescription = destination.contentDescription
172+
)
173+
},
174+
label = { Text(destination.label) }
175+
)
176+
}
177+
}
178+
AppNavHost(navController, startDestination)
179+
}
180+
}
181+
// [END android_compose_components_navigationrailexample]
182+
183+
@OptIn(ExperimentalMaterial3Api::class)
184+
@Preview(showBackground = true)
185+
// [START android_compose_components_navigationtabexample]
186+
@Composable
187+
fun NavigationTabExample(modifier: Modifier = Modifier) {
188+
val navController = rememberNavController()
189+
val startDestination = Destination.SONGS
190+
var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
191+
192+
Scaffold(modifier = modifier) { contentPadding ->
193+
PrimaryTabRow(selectedTabIndex = selectedDestination, modifier = Modifier.padding(contentPadding)) {
194+
Destination.entries.forEachIndexed { index, destination ->
195+
Tab(
196+
selected = selectedDestination == index,
197+
onClick = {
198+
navController.navigate(route = destination.route)
199+
selectedDestination = index
200+
},
201+
text = {
202+
Text(
203+
text = destination.label,
204+
maxLines = 2,
205+
overflow = TextOverflow.Ellipsis
206+
)
207+
}
208+
)
209+
}
210+
}
211+
AppNavHost(navController, startDestination)
212+
}
213+
}
214+
// [END android_compose_components_navigationtabexample]

gradle/libs.versions.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ androidx-paging = "3.3.6"
2121
androidx-startup-runtime = "1.2.0"
2222
androidx-test = "1.6.1"
2323
androidx-test-espresso = "3.6.1"
24+
androidx-test-junit = "1.2.1"
2425
androidx-window = "1.4.0-rc01"
2526
androidx-window-core = "1.4.0-beta02"
2627
androidx-window-java = "1.3.0"
2728
androidxHiltNavigationCompose = "1.2.0"
2829
appcompat = "1.7.0"
2930
coil = "2.7.0"
31+
android-googleid = "1.1.1"
3032
# @keep
3133
compileSdk = "35"
3234
compose-latest = "1.7.8"
@@ -41,6 +43,8 @@ hilt = "2.55"
4143
horologist = "0.6.22"
4244
junit = "4.13.2"
4345
kotlin = "2.1.10"
46+
kotlinxCoroutinesGuava = "1.9.0"
47+
kotlinCoroutinesOkhttp = "1.0"
4448
kotlinxSerializationJson = "1.8.0"
4549
ksp = "2.1.10-1.0.30"
4650
maps-compose = "6.4.4"
@@ -54,13 +58,17 @@ playServicesWearable = "19.0.0"
5458
protolayout = "1.2.1"
5559
recyclerview = "1.4.0"
5660
# @keep
61+
androidx-xr = "1.0.0-alpha02"
5762
targetSdk = "34"
5863
tiles = "1.4.1"
5964
version-catalog-update = "0.8.5"
6065
wear = "1.3.0"
6166
wearComposeFoundation = "1.4.1"
6267
wearComposeMaterial = "1.4.1"
6368
wearToolingPreview = "1.0.0"
69+
activityKtx = "1.10.0"
70+
okHttp = "4.12.0"
71+
webkit = "1.13.0"
6472

6573
[libraries]
6674
accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" }
@@ -124,6 +132,7 @@ androidx-startup-runtime = {module = "androidx.startup:startup-runtime", version
124132
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
125133
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
126134
androidx-test-runner = "androidx.test:runner:1.6.2"
135+
androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-junit" }
127136
androidx-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "tiles" }
128137
androidx-tiles-renderer = { module = "androidx.wear.tiles:tiles-renderer", version.ref = "tiles" }
129138
androidx-tiles-testing = { module = "androidx.wear.tiles:tiles-testing", version.ref = "tiles" }
@@ -135,6 +144,10 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w
135144
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" }
136145
androidx-window-java = {module = "androidx.window:window-java", version.ref = "androidx-window-java" }
137146
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
147+
androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr" }
148+
androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr" }
149+
androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr" }
150+
android-identity-googleid = {module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "android-googleid"}
138151
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
139152
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
140153
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }
@@ -150,11 +163,16 @@ hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.re
150163
horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" }
151164
horologist-compose-material = { module = "com.google.android.horologist:horologist-compose-material", version.ref = "horologist" }
152165
junit = { module = "junit:junit", version.ref = "junit" }
166+
kotlin-coroutines-okhttp = { module = "ru.gildor.coroutines:kotlin-coroutines-okhttp", version.ref = "kotlinCoroutinesOkhttp" }
153167
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
154168
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
169+
kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava" }
155170
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
156171
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
157172
play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" }
173+
androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
174+
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okHttp" }
175+
androidx-webkit = { group = "androidx.webkit", name = "webkit", version.ref = "webkit" }
158176

159177
[plugins]
160178
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }

identity/credentialmanager/build.gradle.kts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
plugins {
22
alias(libs.plugins.android.application)
3+
// [START android_identity_fido2_migration_dependency]
34
alias(libs.plugins.kotlin.android)
5+
// [END android_identity_fido2_migration_dependency]
46
alias(libs.plugins.compose.compiler)
57
}
68

@@ -48,13 +50,26 @@ dependencies {
4850
implementation(libs.androidx.compose.ui.graphics)
4951
implementation(libs.androidx.compose.ui.tooling.preview)
5052
implementation(libs.androidx.compose.material3)
53+
54+
// [START android_identity_credman_dependency]
55+
implementation(libs.androidx.credentials)
56+
// [END android_identity_credman_dependency]
57+
5158
// [START android_identity_gradle_dependencies]
5259
implementation(libs.androidx.credentials)
5360

5461
// optional - needed for credentials support from play services, for devices running
5562
// Android 13 and below.
5663
implementation(libs.androidx.credentials.play.services.auth)
5764
// [END android_identity_gradle_dependencies]
65+
// [START android_identity_siwg_gradle_dependencies]
66+
implementation(libs.androidx.credentials)
67+
implementation(libs.androidx.credentials.play.services.auth)
68+
implementation(libs.android.identity.googleid)
69+
// [END android_identity_siwg_gradle_dependencies]
70+
implementation(libs.okhttp)
71+
implementation(libs.kotlin.coroutines.okhttp)
72+
implementation(libs.androidx.webkit)
5873
debugImplementation(libs.androidx.compose.ui.tooling)
5974
debugImplementation(libs.androidx.compose.ui.test.manifest)
60-
}
75+
}

identity/credentialmanager/src/main/AndroidManifest.xml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
~ limitations under the License.
1616
-->
1717

18-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
18+
<manifest xmlns:tools="http://schemas.android.com/tools"
19+
xmlns:android="http://schemas.android.com/apk/res/android">
1920

2021
<application
2122
android:allowBackup="true"
@@ -38,6 +39,24 @@
3839
<!-- // [START android_identity_assetlinks_manifest] -->
3940
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
4041
<!-- // [END android_identity_assetlinks_manifest] -->
42+
43+
<!-- // [START android_identity_credential_provider_service_manifest] -->
44+
<service android:name=".MyCredentialProviderService"
45+
android:enabled="true"
46+
android:exported="true"
47+
android:label="My Credential Provider"
48+
android:icon="<any drawable icon>"
49+
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
50+
tools:targetApi="upside_down_cake">
51+
<intent-filter>
52+
<action android:name="android.service.credentials.CredentialProviderService"/>
53+
</intent-filter>
54+
<meta-data
55+
android:name="android.credentials.provider"
56+
android:resource="@xml/provider"/>
57+
</service>
58+
<!-- // [END android_identity_credential_provider_service_manifest] -->
59+
4160
</application>
4261

43-
</manifest>
62+
</manifest>

0 commit comments

Comments
 (0)