Skip to content

Commit d0523d3

Browse files
authored
Merge branch 'main' into nav-cujs
2 parents a597454 + 94c2b8f commit d0523d3

File tree

8 files changed

+465
-22
lines changed

8 files changed

+465
-22
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/animations/AnimationSnippets.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,14 +280,15 @@ private fun AnimateAsStateSimple() {
280280
// [START android_compose_animations_animate_as_state]
281281
var enabled by remember { mutableStateOf(true) }
282282

283-
val alpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha")
283+
val animatedAlpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha")
284284
Box(
285285
Modifier
286286
.fillMaxSize()
287-
.graphicsLayer(alpha = alpha)
287+
.graphicsLayer { alpha = animatedAlpha }
288288
.background(Color.Red)
289289
)
290290
// [END android_compose_animations_animate_as_state]
291+
{ Button(onClick = { enabled = !enabled }) { Text("Animate me!") } }
291292
}
292293

293294
@Preview

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ androidx-window-java = "1.3.0"
2727
androidxHiltNavigationCompose = "1.2.0"
2828
appcompat = "1.7.0"
2929
coil = "2.7.0"
30+
android-googleid = "1.1.1"
3031
# @keep
3132
compileSdk = "35"
3233
compose-latest = "1.7.8"
@@ -135,6 +136,7 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w
135136
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" }
136137
androidx-window-java = {module = "androidx.window:window-java", version.ref = "androidx-window-java" }
137138
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
139+
android-identity-googleid = {module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "android-googleid"}
138140
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
139141
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
140142
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }

identity/credentialmanager/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ dependencies {
5555
// Android 13 and below.
5656
implementation(libs.androidx.credentials.play.services.auth)
5757
// [END android_identity_gradle_dependencies]
58+
// [START android_identity_siwg_gradle_dependencies]
59+
implementation(libs.androidx.credentials)
60+
implementation(libs.androidx.credentials.play.services.auth)
61+
implementation(libs.android.identity.googleid)
62+
// [END android_identity_siwg_gradle_dependencies]
5863
debugImplementation(libs.androidx.compose.ui.tooling)
5964
debugImplementation(libs.androidx.compose.ui.test.manifest)
6065
}

identity/credentialmanager/src/main/java/com/example/identity/credentialmanager/PasskeyAndPasswordFunctions.kt

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import android.os.Build
2222
import android.os.Bundle
2323
import android.util.Log
2424
import androidx.annotation.RequiresApi
25+
import androidx.credentials.CreatePasswordRequest
26+
import androidx.credentials.CreatePublicKeyCredentialRequest
2527
import androidx.credentials.CredentialManager
2628
import androidx.credentials.CustomCredential
2729
import androidx.credentials.GetCredentialRequest
@@ -30,7 +32,14 @@ import androidx.credentials.GetPasswordOption
3032
import androidx.credentials.GetPublicKeyCredentialOption
3133
import androidx.credentials.PasswordCredential
3234
import androidx.credentials.PublicKeyCredential
35+
import androidx.credentials.exceptions.CreateCredentialCancellationException
36+
import androidx.credentials.exceptions.CreateCredentialCustomException
37+
import androidx.credentials.exceptions.CreateCredentialException
38+
import androidx.credentials.exceptions.CreateCredentialInterruptedException
39+
import androidx.credentials.exceptions.CreateCredentialProviderConfigurationException
40+
import androidx.credentials.exceptions.CreateCredentialUnknownException
3341
import androidx.credentials.exceptions.GetCredentialException
42+
import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
3443
import kotlinx.coroutines.coroutineScope
3544
import kotlinx.coroutines.runBlocking
3645
import org.json.JSONObject
@@ -43,6 +52,7 @@ class PasskeyAndPasswordFunctions (
4352
// CredentialManager.
4453
private val credentialManager = CredentialManager.create(context)
4554
// [END android_identity_initialize_credman]
55+
private val activityContext = context
4656

4757
// Placeholder for TAG log value.
4858
val TAG = ""
@@ -55,8 +65,7 @@ class PasskeyAndPasswordFunctions (
5565
*/
5666
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
5767
fun signInFlow(
58-
creationResult: JSONObject,
59-
activityContext: Context,
68+
creationResult: JSONObject
6069
) {
6170
val requestJson = creationResult.toString()
6271
// [START android_identity_get_password_passkey_options]
@@ -166,6 +175,93 @@ class PasskeyAndPasswordFunctions (
166175
}
167176
}
168177
// [END android_identity_launch_sign_in_flow_2]
178+
179+
// [START android_identity_create_passkey]
180+
suspend fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
181+
val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
182+
// Contains the request in JSON format. Uses the standard WebAuthn
183+
// web JSON spec.
184+
requestJson = requestJson,
185+
// Defines whether you prefer to use only immediately available
186+
// credentials, not hybrid credentials, to fulfill this request.
187+
// This value is false by default.
188+
preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
189+
)
190+
191+
// Execute CreateCredentialRequest asynchronously to register credentials
192+
// for a user account. Handle success and failure cases with the result and
193+
// exceptions, respectively.
194+
coroutineScope {
195+
try {
196+
val result = credentialManager.createCredential(
197+
// Use an activity-based context to avoid undefined system
198+
// UI launching behavior
199+
context = activityContext,
200+
request = createPublicKeyCredentialRequest,
201+
)
202+
// Handle passkey creation result
203+
} catch (e : CreateCredentialException){
204+
handleFailure(e)
205+
}
206+
}
207+
}
208+
// [END android_identity_create_passkey]
209+
210+
// [START android_identity_handle_create_passkey_failure]
211+
fun handleFailure(e: CreateCredentialException) {
212+
when (e) {
213+
is CreatePublicKeyCredentialDomException -> {
214+
// Handle the passkey DOM errors thrown according to the
215+
// WebAuthn spec.
216+
}
217+
is CreateCredentialCancellationException -> {
218+
// The user intentionally canceled the operation and chose not
219+
// to register the credential.
220+
}
221+
is CreateCredentialInterruptedException -> {
222+
// Retry-able error. Consider retrying the call.
223+
}
224+
is CreateCredentialProviderConfigurationException -> {
225+
// Your app is missing the provider configuration dependency.
226+
// Most likely, you're missing the
227+
// "credentials-play-services-auth" module.
228+
}
229+
is CreateCredentialCustomException -> {
230+
// You have encountered an error from a 3rd-party SDK. If you
231+
// make the API call with a request object that's a subclass of
232+
// CreateCustomCredentialRequest using a 3rd-party SDK, then you
233+
// should check for any custom exception type constants within
234+
// that SDK to match with e.type. Otherwise, drop or log the
235+
// exception.
236+
}
237+
else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
238+
}
239+
}
240+
// [END android_identity_handle_create_passkey_failure]
241+
242+
// [START android_identity_register_password]
243+
suspend fun registerPassword(username: String, password: String) {
244+
// Initialize a CreatePasswordRequest object.
245+
val createPasswordRequest =
246+
CreatePasswordRequest(id = username, password = password)
247+
248+
// Create credential and handle result.
249+
coroutineScope {
250+
try {
251+
val result =
252+
credentialManager.createCredential(
253+
// Use an activity based context to avoid undefined
254+
// system UI launching behavior.
255+
activityContext,
256+
createPasswordRequest
257+
)
258+
// Handle register password result
259+
} catch (e: CreateCredentialException) {
260+
handleFailure(e)
261+
}
262+
}
263+
}
264+
// [END android_identity_register_password]
169265
}
170266

171267
sealed class ExampleCustomCredential {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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.identity.credentialmanager
18+
19+
import android.content.Context
20+
import androidx.credentials.ClearCredentialStateRequest
21+
import androidx.credentials.ClearCredentialStateRequest.Companion.TYPE_CLEAR_RESTORE_CREDENTIAL
22+
import androidx.credentials.CreateRestoreCredentialRequest
23+
import androidx.credentials.CredentialManager
24+
import androidx.credentials.GetCredentialRequest
25+
import androidx.credentials.GetRestoreCredentialOption
26+
27+
class RestoreCredentialsFunctions(
28+
private val context: Context,
29+
private val credentialManager: CredentialManager,
30+
) {
31+
suspend fun createRestoreKey(
32+
createRestoreRequest: CreateRestoreCredentialRequest
33+
) {
34+
// [START android_identity_restore_cred_create]
35+
val credentialManager = CredentialManager.create(context)
36+
37+
// On a successful authentication create a Restore Key
38+
// Pass in the context and CreateRestoreCredentialRequest object
39+
val response = credentialManager.createCredential(context, createRestoreRequest)
40+
// [END android_identity_restore_cred_create]
41+
}
42+
43+
suspend fun getRestoreKey(
44+
fetchAuthenticationJson: () -> String,
45+
) {
46+
// [START android_identity_restore_cred_get]
47+
// Fetch the Authentication JSON from server
48+
val authenticationJson = fetchAuthenticationJson()
49+
50+
// Create the GetRestoreCredentialRequest object
51+
val options = GetRestoreCredentialOption(authenticationJson)
52+
val getRequest = GetCredentialRequest(listOf(options))
53+
54+
// The restore key can be fetched in two scenarios to
55+
// 1. On the first launch of app on the device, fetch the Restore Key
56+
// 2. In the onRestore callback (if the app implements the Backup Agent)
57+
val response = credentialManager.getCredential(context, getRequest)
58+
// [END android_identity_restore_cred_get]
59+
}
60+
61+
suspend fun deleteRestoreKey() {
62+
// [START android_identity_restore_cred_delete]
63+
// Create a ClearCredentialStateRequest object
64+
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
65+
66+
// On user log-out, clear the restore key
67+
val response = credentialManager.clearCredentialState(clearRequest)
68+
// [END android_identity_restore_cred_delete]
69+
}
70+
}

0 commit comments

Comments
 (0)