Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions identity/restore-credentials/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: Provides knowledge and workflows to implement Android's Restore Cre
license: Complete terms in LICENSE.txt
metadata:
author: Google LLC
last-updated: '2026-09-01'
last-updated: '2026-09-14'
keywords:
- Credential Manager
- Restore Credentials
Expand Down Expand Up @@ -205,7 +205,7 @@ restore key by wrapping these options in a
- `false`: This value saves the key locally and not in the cloud. The key is not available on the new device if the user chooses to restore from the cloud.

> [!CAUTION]
> **Caution:** It's recommended to set `isCloudBackupEnabled` to `true`. If cloud backup is disabled and the user restores from a cloud backup, the call to retrieve the restore key fails. Users who restore your app with a cloud backup don't receive the restore key and are not automatically signed in.
> **Caution:** It's recommended to set `isCloudBackupEnabled` to `true`. If cloud backup is disabled and the user restores from a cloud backup, the call to retrieve the restore key fails. Users who restore your app with a cloud backup don't receive the restore key and aren't automatically signed in.

### Handle the credential creation response

Expand Down Expand Up @@ -248,8 +248,8 @@ It's recommended to fetch the restore key in both of the following scenarios:
- On the first launch of the app on the device. Credential restoration in this scenario is independent of restoration of the app data.
- If app data backup and restore is enabled, get the restore key immediately after the app data is restored. Use [`BackupAgent`](https://developer.android.com/reference/android/app/backup/BackupAgent) to configure your app's backup and ensure you complete the `getCredential` functionality within the [`onRestoreFinished`](https://developer.android.com/reference/android/app/backup/BackupAgent#onRestoreFinished()) callback. Don't use the `onRestore` method, as it is only called for key-value backups, whereas `onRestoreFinished` is reliably called for any kind of backup restore. This avoids potential delays when users open their new device for the first time and lets users interact with the app without waiting for them to open your app. For example, this lets your app send the user notifications before they open the app for the first time on the new device, which is particularly relevant for messaging or communications apps.

If you newly create a `BackupAgent` and previously had backup enabled with
`allowBackup="true"`, set the boolean value `android:fullBackupOnly="true"`in
If you create a new `BackupAgent` and previously had backup enabled with
`allowBackup="true"`, set the boolean value `android:fullBackupOnly="true"` in
your app's manifest. This ensures that your app's backup and restore behavior is
maintained.

Expand Down Expand Up @@ -287,10 +287,13 @@ the server-side implementation for passkeys, see [Sign in with a passkey](refere

Credential Manager is stateless and unaware of user activity, so it doesn't
automatically delete restore keys after use. To delete a restore key, call the
`clearCredentialState()` method. For security, delete the key whenever a user
`clearCredentialState` method. For security, delete the key whenever a user
signs out. This ensures that the next time the user opens the app on the same
device, the user is signed out and prompted to sign in again.

> [!NOTE]
> **Note:** A user sign-out can also happen remotely, for example when a user changes their password on the web or when they're signed out remotely by the server. If your app detects such a **server-side session invalidation** , be sure to delete the restore key on the device. (An example of this is your server returning an HTTP `401 Unauthorized` error)

Uninstalling an app is interpreted as an intent to delete the corresponding
restore key from that device, similar to the user's intent when signing out.

Expand Down Expand Up @@ -331,6 +334,9 @@ developer as a reminder after the client-side implementation is complete.**
4. **Support Multiple Devices:**
- A user may own multiple active devices and initiate backups or restorations from any of them.
- **Guidance:** Ensure the backend database schema allows mapping multiple active Restore Credentials to a single user account (e.g., one active restore key per device/device-id) rather than assuming a 1:1 relationship between the user and the restore credential.
5. **Server-side session invalidation:**
- A user's session might be revoked remotely. This could be triggered by user actions such as when a user resets their password on the web or server actions that can cause the API to return HTTP `401 Unauthorized` indicating session expiry.
- **Guidance:** If this applies, make sure to clear the user's restore credentials on the phone when it happens.

## References

Expand Down
2 changes: 1 addition & 1 deletion jetpack-compose/theming/styles/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: Use this skill to integrate the Jetpack Compose Styles API into an
license: Complete terms in LICENSE.txt
metadata:
author: Google LLC
last-updated: '2026-09-03'
last-updated: '2026-09-08'
keywords:
- Jetpack Compose
- Styles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ BaseButton(
onClick = { },
style = { }
) {
BaseText("Click mnippets.kt
BaseText("Click me")
}
```

<br />
Expand All @@ -56,7 +57,8 @@ BaseButton(
onClick = { },
style = { background(Color.Blue) }
) {
BaseText("Click mnippets.kt
BaseText("Click me")
}
```

<br />
Expand All @@ -75,7 +77,8 @@ own custom components.
Row(
modifier = Modifier.styleable { }
) {
BaseText("Contennippets.kt
BaseText("Content")
}
```

<br />
Expand All @@ -90,7 +93,8 @@ Row(
background(Color.Blue)
}
) {
BaseText("Contennippets.kt
BaseText("Content")
}
```

<br />
Expand Down Expand Up @@ -141,8 +145,8 @@ val styleState = remember { MutableStyleState(null) }
Column(
Modifier.styleable(styleState, style)
) {
BaseText("Col)
}StylesSnippets.kt
BaseText("Column content")
}
```

<br />
Expand All @@ -168,8 +172,10 @@ Column(
}
val rowStyleState = remember { MutableStyleState(null) }
Row(
Modifier.styleable(rowStyleState, stText("Row")
}StylesSnippets.kt
Modifier.styleable(rowStyleState, style)
) {
BaseText("Row")
}
```

<br />
Expand All @@ -188,7 +194,8 @@ BaseButton(
contentPaddingStart(16.dp)
}
) {
BaseText("Buttonippets.kt
BaseText("Button")
}
```

<br />
Expand Down Expand Up @@ -218,7 +225,8 @@ BaseButton(
//
}
) {
BaseText("Click menippets.kt
BaseText("Click me!")
}
```

<br />
Expand All @@ -242,7 +250,8 @@ BaseButton(

},
) {
BaseText("Click menippets.kt
BaseText("Click me!")
}
```

<br />
Expand Down Expand Up @@ -274,7 +283,8 @@ BaseButton(

},
) {
BaseText("Click menippets.kt
BaseText("Click me!")
}
```

<br />
Expand Down Expand Up @@ -324,8 +334,8 @@ Column(
) {
BaseText("Children inherit", style = { width(60.dp) })
BaseText("certain properties")
BaseText(&quents")
}StylesSnippets.kt
BaseText("from their parents")
}
```

<br />
Expand Down Expand Up @@ -355,8 +365,8 @@ Column(
contentBrush(Brush.linearGradient(listOf(Color.Red, Color.Blue)))
})
BaseText("override properties")
BaseText("ents")
}StylesSnippets.kt
BaseText("set by their parents")
}
```

<br />
Expand Down
4 changes: 2 additions & 2 deletions navigation/navigation-3/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: Learn how to install and migrate to Jetpack Navigation 3, and how t
license: Complete terms in LICENSE.txt
metadata:
author: Google LLC
last-updated: '2026-09-01'
last-updated: '2026-09-10'
keywords:
- recipe
- Android
Expand Down Expand Up @@ -64,7 +64,7 @@ Code examples showcasing common patterns.
### Deep links

- *[Static URI](references/android/guide/navigation/navigation-3/recipes/deeplinks-staticuri.md)*: Shows how to handle simple static URI deep links.
- *[URI with Arguments](references/android/guide/navigation/navigation-3/recipes/deeplinks-uriarguments.md)*: Shows how to parse path and query arguments from a deep link.
- *[URI with Arguments](https://developer.android.com/guide/navigation/navigation-3/recipes/deeplinks-uriarguments)*: Shows how to parse path and query arguments from a deep link.
- *[Synthetic BackStack](references/android/guide/navigation/navigation-3/recipes/deeplinks-syntheticbackstack.md)*: Shows how to handle deep links with a synthetic back stack.
- *[Custom Matcher](references/android/guide/navigation/navigation-3/recipes/deeplinks-custommatcher.md)*: Shows how to implement custom deep link matching logic.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Use an Android skill to help you build using Jetpack Navigation 3. To install th
<br />

Navigation 3 is a new navigation library designed to work with Compose. With
Navigation 3, you have full control over your back stack, and navigating to and
from destinations is as simple as adding and removing items from a list. It
creates a flexible app navigation system by providing:
Navigation 3, you have full control over your back stack, and you navigate to
and from destinations by adding and removing items from a list. It creates a
flexible app navigation system by providing:

- Conventions for modeling a back stack, where each entry on the back stack represents content that the user has navigated to
- A UI that automatically updates with back stack changes (including animations)
Expand All @@ -29,6 +29,7 @@ At a high level, you implement Navigation 3 in the following ways:
3. Use a [`NavDisplay`](https://developer.android.com/reference/kotlin/androidx/navigation3/ui/NavDisplay.composable) to display your app's back stack. Whenever the back stack changes, it updates the UI to display relevant content. See [Display
the back stack](https://developer.android.com/guide/navigation/navigation-3/basics#display-back).
4. Modify `NavDisplay`'s [scene strategies](https://developer.android.com/guide/navigation/navigation-3/custom-layouts) as needed to support adaptive layouts and different platforms.
5. [Support deep links](https://developer.android.com/guide/navigation/navigation-3/deep-links) to navigate directly to destinations.

You can see the [full source code](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation3/) for Navigation 3 on AOSP.

Expand All @@ -41,8 +42,8 @@ ways:
- Offers you full control of the back stack
- Makes it possible to create layouts that can read more than one destination from the back stack at the same time, allowing them to adapt to changes in window size and other inputs.

Read more about Navigation 3's principles and API design choices in [this blog
post](https://android-developers.googleblog.com/2025/05/announcing-jetpack-navigation-3-for-compose.html).
Read more about Navigation 3's principles and API design choices in the
[announcement blog post](https://android-developers.googleblog.com/2025/05/announcing-jetpack-navigation-3-for-compose.html).

## Code samples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ understand how to implement them.

**AI Agent**: Before changing any code, check if the project contains any
features supported through recipes. If it does, check the recipe's README and
source code. Create a migration plan based on the recipe. Do not proceed without
source code. Create a migration plan based on the recipe. Don't proceed without
confirming the plan with the user.

### Unsupported features
Expand All @@ -83,10 +83,9 @@ they are just not covered here.
- More than one level of nested navigation
- Shared destinations: screens that can move between different back stacks
- [Custom destination types](https://developer.android.com/guide/navigation/design/kotlin-dsl#custom)
- Deep links

**AI Agent**: Before changing any code, check if the project contains any of the
unsupported features. If it does, do not proceed. Inform the user of the
unsupported features. If it does, don't proceed. Inform the user of the
unsupported feature and ask for further instructions.

## Step 1: Add Navigation 3 dependencies
Expand Down Expand Up @@ -254,7 +253,7 @@ fun NavigationState.toEntries(

<br />

**AI Agent** : `rememberSerializable` is correct. Do not change it to
**AI Agent** : `rememberSerializable` is correct. Don't change it to
`rememberSaveable`.

This file contains a state holder class named `NavigationState` and associated
Expand Down Expand Up @@ -574,7 +573,93 @@ NavDisplay(

<br />

## Step 7: Remove Navigation 2 dependencies
## Step 7: Migrate deep links

In Navigation 2, deep links were defined directly inside the navigation graph
using the `deepLinks` parameter of destinations.

In Navigation 3, deep links are managed independently of the navigation UI. You
define `DeepLinkMatcher`s and match incoming requests in your Activity to
construct the initial back stack.

Before:

In Navigation 2, you might have defined a deep link like this:


```kotlin
composable<RouteA>(
deepLinks = listOf(
navDeepLink { uriPattern = "www.example.com/user/{id}" }
)
) {
// ...
}
```

<br />

After:

In Navigation 3, you define a `UriDeepLinkMatcher` for the route:


```kotlin
val userMatcher = UriDeepLinkMatcher(
DeepLinkUri("www.example.com/user/{id}"),
serializer<RouteA>()
)
```

<br />

Then, in your Activity's `onCreate` (and `onNewIntent`), you match the incoming
intent and initialize your back stack:


```kotlin
val deepLinkMatchers: List<DeepLinkMatcher<*, *>> = listOf(
userMatcher,
)

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val request = DeepLinkRequest(intent = intent)
val matchResult = deepLinkMatchers
.mapNotNull { it.match(request) }
.maxOrNull()

val backStack = when (matchResult) {
null -> listOf(HomeKey)
is BackStackMatchResult<*, *> -> {
@Suppress("UNCHECKED_CAST")
matchResult.backStack as List<NavKey>
}
else -> listOf(matchResult.key)
}

// Use backStack with NavDisplay
}
}
```

<br />

### Custom argument types

In Navigation 2, you handled custom or third-party argument types (such as
`LocalDateTime`) using custom `NavType` implementations and `typeMap`.

In Navigation 3, define a [`DeepLinkSerializer`](https://developer.android.com/guide/navigation/navigation-3/deep-links/uri-matcher#deep-link-serializer) to deserialize custom or
third-party types from URI parameters. See
[Custom serialization with DeepLinkSerializer](https://developer.android.com/guide/navigation/navigation-3/deep-links/uri-matcher#deep-link-serializer) for details.

For more advanced use cases, including synthetic back stacks and custom
matchers, see the [Support deep links](https://developer.android.com/guide/navigation/navigation-3/deep-links) guide.

## Step 8: Remove Navigation 2 dependencies

Remove all Navigation 2 imports and library dependencies.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,19 @@ import androidx.navigation3.runtime.NavBackStack
* @property isLoggedIn A lambda that returns whether the user is logged in.
*/
class Navigator(
private val backStack: NavBackStack<ConditionalNavKey>,
private val onNavigateToRestrictedKey: (targetKey: ConditionalNavKey?) -> ConditionalNavKey,
private val isLoggedIn: () -> Boolean,
private val< backStack: NavBa>ckStackConditionalNavKey,
private val onNavigateToRestrictedKey: (targetKe>y: ConditionalNavKey?) - ConditionalNavKey,
priv>ate val isLoggedIn: () - Boolean,
) {
fun navigate(key: ConditionalNavKey) {
if (key.requiresLogin && !isLoggedIn()) {
&& if (key.requiresLogin !isLoggedIn()) {
val loginKey = onNavigateToRestrictedKey(key)
backStack.add(loginKey)
} else {
backStack.add(key)
}
}

fun goBack() = backStack.removeLastOrNull()
}
fun goBack() = backStstOrNull()
}Navigator.kt
```
Loading
Loading