reis-mobile 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +24 -0
- package/README.md +175 -168
- package/THIRD_PARTY_NOTICES.md +224 -4
- package/agents/android-architect.md +89 -0
- package/agents/android-performance-engineer.md +92 -0
- package/agents/android-test-engineer.md +97 -0
- package/agents/flutter-test-engineer.md +2 -0
- package/agents/ios-architect.md +93 -0
- package/agents/ios-performance-engineer.md +92 -0
- package/agents/ios-test-engineer.md +95 -0
- package/agents/lead-mobile.md +10 -1
- package/agents/mobile-staff-engineer.md +1 -1
- package/agents/rn-architect.md +94 -0
- package/agents/rn-performance-engineer.md +92 -0
- package/agents/rn-test-engineer.md +98 -0
- package/bin/reis-mobile.mjs +59 -17
- package/commands/debate.md +10 -1
- package/commands/debug.md +60 -0
- package/commands/reis-mobile.md +7 -2
- package/core/config/project.mjs +59 -0
- package/core/registry/frontmatter.mjs +15 -4
- package/core/registry/registry.mjs +21 -1
- package/core/router/intents.mjs +2 -0
- package/core/router/router.mjs +10 -5
- package/package.json +1 -1
- package/skills/android-adaptive/SKILL.md +304 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/flexbox/container-behavior.md +112 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/flexbox/get-started.md +69 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/flexbox/index.md +81 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/flexbox/item-behavior.md +170 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/grid/container-properties.md +415 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/grid/get-started.md +51 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/grid/index.md +73 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/grid/item-properties.md +170 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/layouts/adaptive/mediaquery/index.md +333 -0
- package/skills/android-adaptive/references/android/develop/ui/compose/tooling/debug.md +114 -0
- package/skills/android-adaptive/references/android/guide/navigation/navigation-3/recipes/material-listdetail.md +141 -0
- package/skills/android-agp-9-upgrade/SKILL.md +106 -0
- package/skills/android-agp-9-upgrade/references/android/build/migrate-to-built-in-kotlin.md +416 -0
- package/skills/android-agp-9-upgrade/references/android/build/releases/agp-9-0-0-release-notes.md +554 -0
- package/skills/android-agp-9-upgrade/references/buildconfig.md +54 -0
- package/skills/android-agp-9-upgrade/references/ksp-kapt.md +39 -0
- package/skills/android-agp-9-upgrade/references/paparazzi-gradle-9.md +30 -0
- package/skills/android-agp-9-upgrade/references/recipes.md +62 -0
- package/skills/android-appfunctions/SKILL.md +74 -0
- package/skills/android-appfunctions/references/adb-interaction-testing.md +95 -0
- package/skills/android-appfunctions/references/context.md +26 -0
- package/skills/android-appfunctions/references/feature-discovery-analysis.md +71 -0
- package/skills/android-appfunctions/references/implementation-configuration.md +282 -0
- package/skills/android-appfunctions/references/kdoc-refinement-optimization.md +75 -0
- package/skills/android-appfunctions/references/migrate-to-service-entry-point.md +226 -0
- package/skills/android-camerax/SKILL.md +135 -0
- package/skills/android-camerax/references/camera1-to-camerax.md +198 -0
- package/skills/android-camerax/references/camera2-to-camerax.md +193 -0
- package/skills/android-camerax/references/expert-blueprints.md +58 -0
- package/skills/android-camerax/references/foldables.md +83 -0
- package/skills/android-camerax/references/immutability.md +60 -0
- package/skills/android-camerax/references/low-light.md +124 -0
- package/skills/android-camerax/references/mlkit-spatial.md +75 -0
- package/skills/android-camerax/references/modern-apis.md +42 -0
- package/skills/android-camerax/references/testing.md +69 -0
- package/skills/android-camerax/references/thermals.md +85 -0
- package/skills/android-camerax/references/wear-os.md +76 -0
- package/skills/android-camerax/references/xr.md +54 -0
- package/skills/android-cli/SKILL.md +474 -0
- package/skills/android-cli/references/interact.md +113 -0
- package/skills/android-cli/references/journeys.md +120 -0
- package/skills/android-code-review/SKILL.md +58 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/SKILL.md +340 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/design/ui/ai-glasses/guides/interaction/inputs.md +67 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/access-hardware-projected-context.md +388 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/buttons.md +139 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/cards.md +148 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/focus.md +107 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/icons.md +90 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/text.md +69 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/jetpack-compose-glimmer/title-chips.md +82 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/android/develop/xr/jetpack-xr-sdk/request-hardware-permissions.md +213 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/button-samples-source.md +176 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/button-source.md +203 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/card-samples-source.md +296 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/card-source.md +515 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/deptheffect-source.md +224 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/deptheffectlevels-source.md +105 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/glimmersansflextypography-source.md +270 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/glimmertheme-source.md +182 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/icon-source.md +388 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/iconbutton-source.md +126 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/iconsizes-source.md +78 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/list-source.md +27 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/listitem-source.md +272 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/liststate-source.md +427 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/material-hct-source.md +217 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/projectedcontext-source.md +265 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/stack-source.md +368 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/stackitemscope-source.md +440 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/stackstate-source.md +325 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/surface-source.md +1211 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/titlechip-samples-source.md +101 -0
- package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/titlechip-source.md +131 -0
- package/skills/android-edge-to-edge/SKILL.md +429 -0
- package/skills/android-engage-sdk-integration/SKILL.md +130 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/faq.md +439 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/food.md +1149 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/healthandfitness.md +1055 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/listen.md +1050 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/otherverticals.md +1301 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/read.md +910 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/shopping.md +1224 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/social.md +719 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/travel.md +1413 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/tv/continue-watching/index.md +19 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/tv/entitlements.md +353 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/tv/getting-started.md +202 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/tv/recommendations.md +471 -0
- package/skills/android-engage-sdk-integration/references/android/guide/playcore/engage/watch.md +1081 -0
- package/skills/android-engage-sdk-integration/references/clusters.md +154 -0
- package/skills/android-engage-sdk-integration/references/common.md +519 -0
- package/skills/android-engage-sdk-integration/references/patterns.md +527 -0
- package/skills/android-engage-sdk-integration/references/requests.md +260 -0
- package/skills/android-engage-sdk-integration/references/schemas/food.md +509 -0
- package/skills/android-engage-sdk-integration/references/schemas/listen.md +842 -0
- package/skills/android-engage-sdk-integration/references/schemas/other.md +220 -0
- package/skills/android-engage-sdk-integration/references/schemas/read.md +412 -0
- package/skills/android-engage-sdk-integration/references/schemas/shopping.md +357 -0
- package/skills/android-engage-sdk-integration/references/schemas/social.md +228 -0
- package/skills/android-engage-sdk-integration/references/schemas/travel.md +923 -0
- package/skills/android-engage-sdk-integration/references/schemas/tv.md +1226 -0
- package/skills/android-engage-sdk-integration/references/schemas/watch.md +1137 -0
- package/skills/android-engage-sdk-integration/references/troubleshooting.md +39 -0
- package/skills/android-gradle-build-debug/SKILL.md +71 -0
- package/skills/android-intent-security/SKILL.md +550 -0
- package/skills/android-leanback-to-compose-tv-migration/SKILL.md +743 -0
- package/skills/android-leanback-to-compose-tv-migration/references/android/training/tv/get-started/navigation.md +372 -0
- package/skills/android-leanback-to-compose-tv-migration/references/android/training/tv/playback/compose/browse.md +243 -0
- package/skills/android-leanback-to-compose-tv-migration/references/android/training/tv/playback/compose/index.md +153 -0
- package/skills/android-media3-cast-integration/SKILL.md +236 -0
- package/skills/android-media3-cast-integration/references/android/media/media3/cast/create-castplayer.md +390 -0
- package/skills/android-media3-cast-integration/references/android/media/media3/cast/customize-castoptions.md +93 -0
- package/skills/android-media3-cast-integration/references/android/media/media3/cast/index.md +21 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/SKILL.md +127 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/analysis-of-the-project-and-layout.md +42 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/android/develop/ui/compose/designsystems/migrate-xml-theme-to-compose.md +171 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/android/develop/ui/compose/migrate/interoperability-apis/compose-in-views.md +299 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/android/develop/ui/compose/migrate/interoperability-apis/views-in-compose.md +286 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/android/develop/ui/compose/setup-compose-dependencies-and-compiler.md +197 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/identify-optimal-xml-candidate.md +31 -0
- package/skills/android-migrate-xml-views-to-jetpack-compose/references/xml-layout-migration.md +86 -0
- package/skills/android-ml-kit-genai-prompt-api/SKILL.md +124 -0
- package/skills/android-ml-kit-genai-prompt-api/references/get-started.md +272 -0
- package/skills/android-ml-kit-genai-prompt-api/references/prefix-caching.md +146 -0
- package/skills/android-ml-kit-genai-prompt-api/references/prompt-design.md +55 -0
- package/skills/android-ml-kit-genai-prompt-api/references/prompt-optimization.md +74 -0
- package/skills/android-ml-kit-genai-prompt-api/references/structured-output.md +219 -0
- package/skills/android-ml-kit-genai-prompt-api/references/system-instructions.md +71 -0
- package/skills/android-navigation-3/SKILL.md +122 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/index.md +51 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/migration-guide.md +670 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/animations.md +147 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/basic.md +89 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/basicdsl.md +85 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/basicsaveable.md +90 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/bottomsheet.md +195 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/common-ui.md +200 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/conditional-transitions.md +182 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/conditional.md +230 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/deeplinks-custommatcher.md +176 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/deeplinks-staticuri.md +140 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/deeplinks-syntheticbackstack.md +184 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/dialog.md +107 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/lifecycle-owner.md +185 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/material-listdetail.md +141 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/material-supportingpane.md +145 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/modular-hilt.md +283 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/modular-koin.md +287 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/multiple-backstacks.md +468 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/passingarguments.md +371 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/results-event.md +273 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/results-state.md +266 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/scenes-listdetail.md +435 -0
- package/skills/android-navigation-3/references/android/guide/navigation/navigation-3/recipes/scenes-twopane.md +244 -0
- package/skills/android-navigation-3/references/android/guide/navigation/type-safe-destinations.md +129 -0
- package/skills/android-navigation-event/SKILL.md +312 -0
- package/skills/android-navigation-event/references/android/guide/navigation/navigation-event/dispatcher.md +134 -0
- package/skills/android-navigation-event/references/android/guide/navigation/navigation-event/handle-back.md +217 -0
- package/skills/android-navigation-event/references/android/guide/navigation/navigation-event/index.md +83 -0
- package/skills/android-navigation-event/references/android/guide/navigation/navigation-event/setup.md +32 -0
- package/skills/android-play-billing-library-version-upgrade/SKILL.md +96 -0
- package/skills/android-play-billing-library-version-upgrade/references/android/google/play/billing/release-notes.md +1240 -0
- package/skills/android-play-billing-library-version-upgrade/references/migration-logic.md +78 -0
- package/skills/android-play-billing-library-version-upgrade/references/version-checklist.md +54 -0
- package/skills/android-play-policy-insights/SKILL.md +172 -0
- package/skills/android-play-policy-insights/resources/common_mandates.md +100 -0
- package/skills/android-play-policy-insights/resources/compliance_report_template.md +59 -0
- package/skills/android-play-policy-insights/resources/critic.md +86 -0
- package/skills/android-play-policy-insights/resources/goal_data_safety.md +212 -0
- package/skills/android-play-policy-insights/resources/goal_permissions_and_apis.md +459 -0
- package/skills/android-play-policy-insights/resources/goal_user_account.md +106 -0
- package/skills/android-play-policy-insights/resources/policies.json +321 -0
- package/skills/android-play-policy-insights/resources/scanner_config.json +843 -0
- package/skills/android-play-policy-insights/scripts/generate_report.py +860 -0
- package/skills/android-play-policy-insights/scripts/orchestrator.py +1313 -0
- package/skills/android-play-policy-insights/scripts/play_store_scraper.py +410 -0
- package/skills/android-play-policy-insights/scripts/scanner.py +261 -0
- package/skills/android-play-policy-insights/scripts/template_engine.py +243 -0
- package/skills/android-profiler/SKILL.md +60 -0
- package/skills/android-profiler/analysis/analysis_orchestrator.md +32 -0
- package/skills/android-profiler/analysis/workflows/perfetto-sql/perfetto_sql.md +28 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/perfetto_trace_analysis.md +121 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/guiding_principles.md +51 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/domain_hints_template.md +7 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_cpu.md +56 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_graphics.md +51 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_io.md +46 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_ipc.md +41 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_memory.md +55 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/hints/subsystems/hints_power.md +33 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/per_candidate_analysis.md +172 -0
- package/skills/android-profiler/analysis/workflows/perfetto-trace-analysis/references/triage.md +94 -0
- package/skills/android-profiler/recording/recording_orchestrator.md +36 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/perfetto_trace_recording.md +148 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/app_jank.pftxt +43 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/cpu_profile.pftxt +35 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/java_heap_dump.pftxt +21 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/long_background.pftxt +37 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/memory_counters.pftxt +45 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/native_heap.pftxt +21 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/example-configs/sched_cpu.pftxt +33 -0
- package/skills/android-profiler/recording/workflows/perfetto-trace-recording/references/trace_config_reference.md +102 -0
- package/skills/android-profiler/references/env_setup.md +15 -0
- package/skills/android-profiler/references/perfetto/setup.md +29 -0
- package/skills/android-profiler/references/perfetto/sql.md +327 -0
- package/skills/android-r8-analyzer/SKILL.md +72 -0
- package/skills/android-r8-analyzer/references/CONFIGURATION-ANALYZER.md +287 -0
- package/skills/android-r8-analyzer/references/CONFIGURATION.md +44 -0
- package/skills/android-r8-analyzer/references/KEEP-RULES-IMPACT-HIERARCHY.md +83 -0
- package/skills/android-r8-analyzer/references/REDUNDANT-RULES.md +222 -0
- package/skills/android-r8-analyzer/references/REFLECTION-GUIDE.md +139 -0
- package/skills/android-r8-analyzer/references/REPORT_FORMAT.md +51 -0
- package/skills/android-r8-analyzer/references/android/topic/performance/app-optimization/enable-app-optimization.md +252 -0
- package/skills/android-r8-analyzer/references/android/training/testing/other-components/ui-automator.md +312 -0
- package/skills/android-restore-credentials/SKILL.md +359 -0
- package/skills/android-restore-credentials/references/android/guide/topics/manifest/application-element.md +832 -0
- package/skills/android-restore-credentials/references/android/identity/passkeys/create-passkeys.md +327 -0
- package/skills/android-restore-credentials/references/android/identity/passkeys/sign-in-with-passkeys.md +219 -0
- package/skills/android-styles/SKILL.md +229 -0
- package/skills/android-styles/references/android/develop/ui/compose/designsystems/custom.md +459 -0
- package/skills/android-styles/references/android/develop/ui/compose/styles/fundamentals.md +421 -0
- package/skills/android-styles/references/android/develop/ui/compose/styles/state-animations.md +461 -0
- package/skills/android-styles/references/android/develop/ui/compose/styles/styles-vs-modifiers.md +48 -0
- package/skills/android-styles/references/android/develop/ui/compose/styles/theming.md +269 -0
- package/skills/android-verified-email/SKILL.md +432 -0
- package/skills/android-verified-email/references/android/identity/credential-manager/index.md +67 -0
- package/skills/android-verified-email/references/android/identity/digital-credentials/credential-verifier.md +166 -0
- package/skills/android-verified-email/references/android/identity/digital-credentials/email-verification-implementation.md +313 -0
- package/skills/android-verified-email/references/android/identity/digital-credentials/email-verification.md +158 -0
- package/skills/android-verified-email/references/android/identity/digital-credentials/index.md +112 -0
- package/skills/android-verified-email/references/android/identity/passkeys/create-passkeys.md +327 -0
- package/skills/android-verified-email/references/android/identity/sign-in/credential-manager-webview.md +84 -0
- package/skills/android-wear-compose-m3/SKILL.md +365 -0
- package/skills/android-wear-compose-m3/references/android/training/wearables/compose/migrate-to-material3.md +677 -0
- package/skills/android-wear-compose-m3/references/foundation/AmbientModeSample.kt.md +123 -0
- package/skills/android-wear-compose-m3/references/foundation/AmbientModeSample.kt.md.txt +123 -0
- package/skills/android-wear-compose-m3/references/foundation/CurvedWorldSample.kt.md +430 -0
- package/skills/android-wear-compose-m3/references/foundation/CurvedWorldSample.kt.md.txt +430 -0
- package/skills/android-wear-compose-m3/references/foundation/ExpandableSample.kt.md +99 -0
- package/skills/android-wear-compose-m3/references/foundation/ExpandableSample.kt.md.txt +99 -0
- package/skills/android-wear-compose-m3/references/foundation/HierarchicalFocusSample.kt.md +141 -0
- package/skills/android-wear-compose-m3/references/foundation/HierarchicalFocusSample.kt.md.txt +141 -0
- package/skills/android-wear-compose-m3/references/foundation/PagerSamples.kt.md +56 -0
- package/skills/android-wear-compose-m3/references/foundation/PagerSamples.kt.md.txt +56 -0
- package/skills/android-wear-compose-m3/references/foundation/RotarySamples.kt.md +153 -0
- package/skills/android-wear-compose-m3/references/foundation/RotarySamples.kt.md.txt +153 -0
- package/skills/android-wear-compose-m3/references/foundation/ScalingLazyColumnSample.kt.md +131 -0
- package/skills/android-wear-compose-m3/references/foundation/ScalingLazyColumnSample.kt.md.txt +131 -0
- package/skills/android-wear-compose-m3/references/foundation/SwipeToDismissBoxSample.kt.md +169 -0
- package/skills/android-wear-compose-m3/references/foundation/SwipeToDismissBoxSample.kt.md.txt +169 -0
- package/skills/android-wear-compose-m3/references/foundation/SwipeToRevealSample.kt.md +282 -0
- package/skills/android-wear-compose-m3/references/foundation/SwipeToRevealSample.kt.md.txt +282 -0
- package/skills/android-wear-compose-m3/references/foundation/TransformingLazyColumnSample.kt.md +335 -0
- package/skills/android-wear-compose-m3/references/foundation/TransformingLazyColumnSample.kt.md.txt +335 -0
- package/skills/android-wear-compose-m3/references/material3/AlertDialogSample.kt.md +377 -0
- package/skills/android-wear-compose-m3/references/material3/AlertDialogSample.kt.md.txt +377 -0
- package/skills/android-wear-compose-m3/references/material3/AnimatedTextSample.kt.md +165 -0
- package/skills/android-wear-compose-m3/references/material3/AnimatedTextSample.kt.md.txt +165 -0
- package/skills/android-wear-compose-m3/references/material3/ButtonGroupSample.kt.md +91 -0
- package/skills/android-wear-compose-m3/references/material3/ButtonGroupSample.kt.md.txt +91 -0
- package/skills/android-wear-compose-m3/references/material3/ButtonSample.kt.md +496 -0
- package/skills/android-wear-compose-m3/references/material3/ButtonSample.kt.md.txt +496 -0
- package/skills/android-wear-compose-m3/references/material3/CardSample.kt.md +470 -0
- package/skills/android-wear-compose-m3/references/material3/CardSample.kt.md.txt +470 -0
- package/skills/android-wear-compose-m3/references/material3/CheckboxButtonSample.kt.md +67 -0
- package/skills/android-wear-compose-m3/references/material3/CheckboxButtonSample.kt.md.txt +67 -0
- package/skills/android-wear-compose-m3/references/material3/ConfirmationDialogSample.kt.md +162 -0
- package/skills/android-wear-compose-m3/references/material3/ConfirmationDialogSample.kt.md.txt +162 -0
- package/skills/android-wear-compose-m3/references/material3/CurvedTextSamples.kt.md +78 -0
- package/skills/android-wear-compose-m3/references/material3/CurvedTextSamples.kt.md.txt +78 -0
- package/skills/android-wear-compose-m3/references/material3/DatePickerSample.kt.md +131 -0
- package/skills/android-wear-compose-m3/references/material3/DatePickerSample.kt.md.txt +131 -0
- package/skills/android-wear-compose-m3/references/material3/DynamicColorSchemeSample.kt.md +74 -0
- package/skills/android-wear-compose-m3/references/material3/DynamicColorSchemeSample.kt.md.txt +74 -0
- package/skills/android-wear-compose-m3/references/material3/EdgeButtonSample.kt.md +63 -0
- package/skills/android-wear-compose-m3/references/material3/EdgeButtonSample.kt.md.txt +63 -0
- package/skills/android-wear-compose-m3/references/material3/FadingExpandingLabelSample.kt.md +60 -0
- package/skills/android-wear-compose-m3/references/material3/FadingExpandingLabelSample.kt.md.txt +60 -0
- package/skills/android-wear-compose-m3/references/material3/IconButtonSample.kt.md +125 -0
- package/skills/android-wear-compose-m3/references/material3/IconButtonSample.kt.md.txt +125 -0
- package/skills/android-wear-compose-m3/references/material3/IconToggleButtonSample.kt.md +116 -0
- package/skills/android-wear-compose-m3/references/material3/IconToggleButtonSample.kt.md.txt +116 -0
- package/skills/android-wear-compose-m3/references/material3/LevelIndicatorSample.kt.md +68 -0
- package/skills/android-wear-compose-m3/references/material3/LevelIndicatorSample.kt.md.txt +68 -0
- package/skills/android-wear-compose-m3/references/material3/LinearProgressIndicatorSample.kt.md +50 -0
- package/skills/android-wear-compose-m3/references/material3/LinearProgressIndicatorSample.kt.md.txt +50 -0
- package/skills/android-wear-compose-m3/references/material3/ListHeaderSample.kt.md +177 -0
- package/skills/android-wear-compose-m3/references/material3/ListHeaderSample.kt.md.txt +177 -0
- package/skills/android-wear-compose-m3/references/material3/OneHandedGestureSamples.kt.md +651 -0
- package/skills/android-wear-compose-m3/references/material3/OneHandedGestureSamples.kt.md.txt +651 -0
- package/skills/android-wear-compose-m3/references/material3/OpenOnPhoneDialogSample.kt.md +57 -0
- package/skills/android-wear-compose-m3/references/material3/OpenOnPhoneDialogSample.kt.md.txt +57 -0
- package/skills/android-wear-compose-m3/references/material3/PageIndicatorSample.kt.md +110 -0
- package/skills/android-wear-compose-m3/references/material3/PageIndicatorSample.kt.md.txt +110 -0
- package/skills/android-wear-compose-m3/references/material3/PagerScaffoldSample.kt.md +204 -0
- package/skills/android-wear-compose-m3/references/material3/PagerScaffoldSample.kt.md.txt +204 -0
- package/skills/android-wear-compose-m3/references/material3/PickerGroupSample.kt.md +139 -0
- package/skills/android-wear-compose-m3/references/material3/PickerGroupSample.kt.md.txt +139 -0
- package/skills/android-wear-compose-m3/references/material3/PickerSample.kt.md +111 -0
- package/skills/android-wear-compose-m3/references/material3/PickerSample.kt.md.txt +111 -0
- package/skills/android-wear-compose-m3/references/material3/PlaceholderSample.kt.md +184 -0
- package/skills/android-wear-compose-m3/references/material3/PlaceholderSample.kt.md.txt +184 -0
- package/skills/android-wear-compose-m3/references/material3/ProgressIndicatorSample.kt.md +263 -0
- package/skills/android-wear-compose-m3/references/material3/ProgressIndicatorSample.kt.md.txt +263 -0
- package/skills/android-wear-compose-m3/references/material3/RadioButtonSample.kt.md +105 -0
- package/skills/android-wear-compose-m3/references/material3/RadioButtonSample.kt.md.txt +105 -0
- package/skills/android-wear-compose-m3/references/material3/ScaffoldSample.kt.md +146 -0
- package/skills/android-wear-compose-m3/references/material3/ScaffoldSample.kt.md.txt +146 -0
- package/skills/android-wear-compose-m3/references/material3/ScrollAwaySample.kt.md +97 -0
- package/skills/android-wear-compose-m3/references/material3/ScrollAwaySample.kt.md.txt +97 -0
- package/skills/android-wear-compose-m3/references/material3/ScrollIndicatorSample.kt.md +66 -0
- package/skills/android-wear-compose-m3/references/material3/ScrollIndicatorSample.kt.md.txt +66 -0
- package/skills/android-wear-compose-m3/references/material3/SliderSample.kt.md +86 -0
- package/skills/android-wear-compose-m3/references/material3/SliderSample.kt.md.txt +86 -0
- package/skills/android-wear-compose-m3/references/material3/StepperSample.kt.md +148 -0
- package/skills/android-wear-compose-m3/references/material3/StepperSample.kt.md.txt +148 -0
- package/skills/android-wear-compose-m3/references/material3/SurfaceTransformationSample.kt.md +166 -0
- package/skills/android-wear-compose-m3/references/material3/SurfaceTransformationSample.kt.md.txt +166 -0
- package/skills/android-wear-compose-m3/references/material3/SwipeToDismissBoxSample.kt.md +172 -0
- package/skills/android-wear-compose-m3/references/material3/SwipeToDismissBoxSample.kt.md.txt +172 -0
- package/skills/android-wear-compose-m3/references/material3/SwipeToRevealSample.kt.md +367 -0
- package/skills/android-wear-compose-m3/references/material3/SwipeToRevealSample.kt.md.txt +367 -0
- package/skills/android-wear-compose-m3/references/material3/SwitchButtonSample.kt.md +66 -0
- package/skills/android-wear-compose-m3/references/material3/SwitchButtonSample.kt.md.txt +66 -0
- package/skills/android-wear-compose-m3/references/material3/TextButtonSample.kt.md +115 -0
- package/skills/android-wear-compose-m3/references/material3/TextButtonSample.kt.md.txt +115 -0
- package/skills/android-wear-compose-m3/references/material3/TextToggleButtonSample.kt.md +71 -0
- package/skills/android-wear-compose-m3/references/material3/TextToggleButtonSample.kt.md.txt +71 -0
- package/skills/android-wear-compose-m3/references/material3/TimePickerSample.kt.md +151 -0
- package/skills/android-wear-compose-m3/references/material3/TimePickerSample.kt.md.txt +151 -0
- package/skills/android-wear-compose-m3/references/material3/TimeTextSample.kt.md +65 -0
- package/skills/android-wear-compose-m3/references/material3/TimeTextSample.kt.md.txt +65 -0
- package/skills/android-wear-compose-m3/references/material3/TransformationSpecSample.kt.md +246 -0
- package/skills/android-wear-compose-m3/references/material3/TransformationSpecSample.kt.md.txt +246 -0
- package/skills/android-wear-compose-m3/references/material3/TransformingLazyColumnNotificationsSample.kt.md +89 -0
- package/skills/android-wear-compose-m3/references/material3/TransformingLazyColumnNotificationsSample.kt.md.txt +89 -0
- package/skills/android-wear-compose-m3/references/material3/TransformingLazyColumnSample.kt.md +309 -0
- package/skills/android-wear-compose-m3/references/material3/TransformingLazyColumnSample.kt.md.txt +309 -0
- package/skills/android-wear-compose-m3/references/navigation3/SwipeDismissableSceneStrategySample.kt.md +205 -0
- package/skills/android-wear-compose-m3/references/navigation3/SwipeDismissableSceneStrategySample.kt.md.txt +205 -0
- package/skills/{architecture-feature-first → flutter-architecture-feature-first}/SKILL.md +1 -1
- package/skills/{bloc → flutter-bloc}/SKILL.md +1 -1
- package/skills/flutter-build-debug/SKILL.md +66 -0
- package/skills/{code-review → flutter-code-review}/SKILL.md +1 -1
- package/skills/{dart-3-updates → flutter-dart-3-updates}/SKILL.md +1 -1
- package/skills/{dart-fix-runtime-errors → flutter-dart-fix-runtime-errors}/SKILL.md +1 -1
- package/skills/{dart-resolve-package-conflicts → flutter-dart-resolve-package-conflicts}/SKILL.md +1 -1
- package/skills/{dart-run-static-analysis → flutter-dart-run-static-analysis}/SKILL.md +1 -1
- package/skills/{dart-use-pattern-matching → flutter-dart-use-pattern-matching}/SKILL.md +1 -1
- package/skills/{effective-dart → flutter-effective-dart}/SKILL.md +1 -1
- package/skills/{provider → flutter-provider}/SKILL.md +1 -1
- package/skills/{riverpod → flutter-riverpod}/SKILL.md +1 -1
- package/skills/ios-cocoapods-debug/SKILL.md +65 -0
- package/skills/ios-code-review/SKILL.md +56 -0
- package/skills/ios-xcode-build-debug/SKILL.md +66 -0
- package/skills/{detect-mobile-stack → mobile-detect-stack}/SKILL.md +1 -1
- package/skills/mobile-firebase/SKILL.md +54 -0
- package/skills/mobile-firebase/references/android/ai-logic.md +31 -0
- package/skills/mobile-firebase/references/android/analytics.md +33 -0
- package/skills/mobile-firebase/references/android/app-check.md +36 -0
- package/skills/mobile-firebase/references/android/auth.md +44 -0
- package/skills/mobile-firebase/references/android/crashlytics.md +31 -0
- package/skills/mobile-firebase/references/android/firestore.md +44 -0
- package/skills/mobile-firebase/references/android/functions.md +33 -0
- package/skills/mobile-firebase/references/android/in-app-messaging.md +28 -0
- package/skills/mobile-firebase/references/android/messaging.md +41 -0
- package/skills/mobile-firebase/references/android/realtime-database.md +50 -0
- package/skills/mobile-firebase/references/android/remote-config.md +43 -0
- package/skills/mobile-firebase/references/android/sql-connect.md +31 -0
- package/skills/mobile-firebase/references/android/storage.md +39 -0
- package/skills/mobile-firebase/references/android.md +30 -0
- package/skills/{firebase-ai/SKILL.md → mobile-firebase/references/flutter/ai-logic.md} +2 -8
- package/skills/{firebase-analytics/SKILL.md → mobile-firebase/references/flutter/analytics.md} +2 -8
- package/skills/{firebase-app-check/SKILL.md → mobile-firebase/references/flutter/app-check.md} +2 -8
- package/skills/{firebase-auth/SKILL.md → mobile-firebase/references/flutter/auth.md} +2 -8
- package/skills/{firebase-crashlytics/SKILL.md → mobile-firebase/references/flutter/crashlytics.md} +2 -8
- package/skills/{firebase-cloud-firestore/SKILL.md → mobile-firebase/references/flutter/firestore.md} +2 -8
- package/skills/{firebase-cloud-functions/SKILL.md → mobile-firebase/references/flutter/functions.md} +2 -8
- package/skills/{firebase-in-app-messaging/SKILL.md → mobile-firebase/references/flutter/in-app-messaging.md} +2 -8
- package/skills/{firebase-messaging/SKILL.md → mobile-firebase/references/flutter/messaging.md} +2 -8
- package/skills/{firebase-database/SKILL.md → mobile-firebase/references/flutter/realtime-database.md} +2 -8
- package/skills/{firebase-remote-config/SKILL.md → mobile-firebase/references/flutter/remote-config.md} +2 -8
- package/skills/{flutterfire-configure/SKILL.md → mobile-firebase/references/flutter/setup-flutterfire.md} +1 -8
- package/skills/{firebase-data-connect/SKILL.md → mobile-firebase/references/flutter/sql-connect.md} +2 -8
- package/skills/{firebase-storage/SKILL.md → mobile-firebase/references/flutter/storage.md} +2 -8
- package/skills/mobile-firebase/references/flutter.md +36 -0
- package/skills/mobile-firebase/references/ios/ai-logic.md +30 -0
- package/skills/mobile-firebase/references/ios/analytics.md +33 -0
- package/skills/mobile-firebase/references/ios/app-check.md +36 -0
- package/skills/mobile-firebase/references/ios/auth.md +48 -0
- package/skills/mobile-firebase/references/ios/crashlytics.md +34 -0
- package/skills/mobile-firebase/references/ios/firestore.md +53 -0
- package/skills/mobile-firebase/references/ios/functions.md +35 -0
- package/skills/mobile-firebase/references/ios/in-app-messaging.md +26 -0
- package/skills/mobile-firebase/references/ios/messaging.md +45 -0
- package/skills/mobile-firebase/references/ios/realtime-database.md +49 -0
- package/skills/mobile-firebase/references/ios/remote-config.md +44 -0
- package/skills/mobile-firebase/references/ios/sql-connect.md +33 -0
- package/skills/mobile-firebase/references/ios/storage.md +36 -0
- package/skills/mobile-firebase/references/ios.md +28 -0
- package/skills/mobile-firebase/references/react-native/ai-logic.md +32 -0
- package/skills/mobile-firebase/references/react-native/analytics.md +43 -0
- package/skills/mobile-firebase/references/react-native/app-check.md +35 -0
- package/skills/mobile-firebase/references/react-native/auth.md +50 -0
- package/skills/mobile-firebase/references/react-native/crashlytics.md +36 -0
- package/skills/mobile-firebase/references/react-native/firestore.md +45 -0
- package/skills/mobile-firebase/references/react-native/functions.md +28 -0
- package/skills/mobile-firebase/references/react-native/in-app-messaging.md +27 -0
- package/skills/mobile-firebase/references/react-native/messaging.md +46 -0
- package/skills/mobile-firebase/references/react-native/realtime-database.md +46 -0
- package/skills/mobile-firebase/references/react-native/remote-config.md +32 -0
- package/skills/mobile-firebase/references/react-native/sql-connect.md +11 -0
- package/skills/mobile-firebase/references/react-native/storage.md +30 -0
- package/skills/mobile-firebase/references/react-native.md +29 -0
- package/skills/mobile-test/SKILL.md +63 -0
- package/skills/mobile-test/references/android/setup/GUIDE.md +183 -0
- package/skills/mobile-test/references/android/setup/references/android/develop/ui/compose/testing/common-patterns.md +181 -0
- package/skills/mobile-test/references/android/setup/references/android/studio/preview/compose-screenshot-testing.md +255 -0
- package/skills/mobile-test/references/android/setup/references/android/training/dependency-injection/hilt-testing.md +447 -0
- package/skills/mobile-test/references/android.md +83 -0
- package/skills/{dart-collect-coverage/SKILL.md → mobile-test/references/flutter/coverage.md} +2 -11
- package/skills/{dart-generate-test-mocks/SKILL.md → mobile-test/references/flutter/generate-mocks.md} +2 -11
- package/skills/{flutter-add-integration-test/SKILL.md → mobile-test/references/flutter/integration-tests.md} +2 -11
- package/skills/{dart-migrate-to-checks-package/SKILL.md → mobile-test/references/flutter/migrate-to-checks.md} +2 -13
- package/skills/{mockito/SKILL.md → mobile-test/references/flutter/mockito.md} +1 -8
- package/skills/{mocktail/SKILL.md → mobile-test/references/flutter/mocktail.md} +1 -8
- package/skills/{patrol-e2e-testing/SKILL.md → mobile-test/references/flutter/patrol.md} +1 -8
- package/skills/{testing/SKILL.md → mobile-test/references/flutter/testing-guidelines.md} +1 -8
- package/skills/{dart-add-unit-test/SKILL.md → mobile-test/references/flutter/unit-tests.md} +2 -11
- package/skills/{flutter-add-widget-test/SKILL.md → mobile-test/references/flutter/widget-tests.md} +2 -11
- package/skills/mobile-test/references/flutter.md +44 -0
- package/skills/mobile-test/references/ios.md +79 -0
- package/skills/mobile-test/references/react-native.md +62 -0
- /package/skills/{dart-use-pattern-matching → flutter-dart-use-pattern-matching}/examples/json_patterns.dart +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
When using a Projected Context to launch the Glasses Projected activity on the
|
|
2
|
+
Projected Device, refer to the following source code in `ProjectedContext.kt`:
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
```kotlin
|
|
6
|
+
/*
|
|
7
|
+
* Copyright 2025 The Android Open Source Project
|
|
8
|
+
*
|
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
* you may not use this file except in compliance with the License.
|
|
11
|
+
* You may obtain a copy of the License at
|
|
12
|
+
*
|
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
*
|
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
* See the License for the specific language governing permissions and
|
|
19
|
+
* limitations under the License.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
package androidx.xr.projected
|
|
23
|
+
|
|
24
|
+
import android.app.ActivityOptions
|
|
25
|
+
import android.companion.virtual.VirtualDeviceManager
|
|
26
|
+
import android.content.Context
|
|
27
|
+
import android.hardware.display.DisplayManager
|
|
28
|
+
import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
|
|
29
|
+
import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
|
|
30
|
+
import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
|
|
31
|
+
import android.os.Build
|
|
32
|
+
import androidx.annotation.RequiresApi
|
|
33
|
+
import androidx.annotation.VisibleForTesting
|
|
34
|
+
import androidx.xr.projected.experimental.ExperimentalProjectedApi
|
|
35
|
+
import kotlin.coroutines.CoroutineContext
|
|
36
|
+
import kotlinx.coroutines.CoroutineDispatcher
|
|
37
|
+
import kotlinx.coroutines.asExecutor
|
|
38
|
+
import kotlinx.coroutines.channels.awaitClose
|
|
39
|
+
import kotlinx.coroutines.flow.Flow
|
|
40
|
+
import kotlinx.coroutines.flow.callbackFlow
|
|
41
|
+
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper for accessing Projected device [Context] and its features.
|
|
45
|
+
*
|
|
46
|
+
* Projected device is an XR device connected to an Android device (host). Host can project the
|
|
47
|
+
* application content to the Projected device and let users interact with it.
|
|
48
|
+
*
|
|
49
|
+
* The Projected device context will ensure Projected device system services are returned, when
|
|
50
|
+
* queried for system services from this object.
|
|
51
|
+
*
|
|
52
|
+
* Note: The application context's deviceId can switch between the Projected and host deviceId
|
|
53
|
+
* depending on which activity was most recently in the foreground. Prefer using the Activity
|
|
54
|
+
* context to minimize the risk of running into this problem.
|
|
55
|
+
*/
|
|
56
|
+
@ExperimentalProjectedApi
|
|
57
|
+
public object ProjectedContext {
|
|
58
|
+
|
|
59
|
+
private const val TAG = "ProjectedContext"
|
|
60
|
+
|
|
61
|
+
@VisibleForTesting internal const val PROJECTED_DEVICE_NAME = "ProjectionDevice"
|
|
62
|
+
@VisibleForTesting internal const val PROJECTED_DISPLAY_NAME = "ProjectionDisplay"
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Explicitly create the Projected device context from any context object.
|
|
66
|
+
*
|
|
67
|
+
* @throws IllegalStateException if the projected device was not found.
|
|
68
|
+
*/
|
|
69
|
+
@JvmStatic
|
|
70
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
71
|
+
public fun createProjectedDeviceContext(context: Context): Context {
|
|
72
|
+
val deviceId =
|
|
73
|
+
getProjectedDeviceId(context)
|
|
74
|
+
?: throw IllegalStateException("Projected device not found.")
|
|
75
|
+
return context.createDeviceContext(deviceId)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Explicitly create the host device context from any context object. The host is the device
|
|
80
|
+
* that connects to a Projected device.
|
|
81
|
+
*
|
|
82
|
+
* If an application is using a Projected device context and it wants to use system services
|
|
83
|
+
* from the host (e.g. phone), it needs to use the host device context.
|
|
84
|
+
*/
|
|
85
|
+
@JvmStatic
|
|
86
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
87
|
+
public fun createHostDeviceContext(context: Context): Context =
|
|
88
|
+
context.createDeviceContext(Context.DEVICE_ID_DEFAULT)
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns the name of the Projected device or null if either virtual device wasn't found or the
|
|
92
|
+
* name of the virtual device wasn't set.
|
|
93
|
+
*
|
|
94
|
+
* @throws IllegalArgumentException If another context is used (e.g. the host context).
|
|
95
|
+
*/
|
|
96
|
+
@JvmStatic
|
|
97
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
98
|
+
public fun getProjectedDeviceName(context: Context): String? =
|
|
99
|
+
// TODO: b/424812882 - Turn this into a lint check with an annotation.
|
|
100
|
+
if (isProjectedDeviceContext(context)) {
|
|
101
|
+
getVirtualDevice(context)?.name
|
|
102
|
+
} else {
|
|
103
|
+
throw IllegalArgumentException(
|
|
104
|
+
"Provided context is not the Projected device context. Can't get the device name."
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Returns whether the provided context is the Projected device context. */
|
|
109
|
+
@JvmStatic
|
|
110
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
111
|
+
public fun isProjectedDeviceContext(context: Context): Boolean =
|
|
112
|
+
getVirtualDevice(context)?.name?.startsWith(PROJECTED_DEVICE_NAME) == true
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Creates [ActivityOptions] that should be used to start an activity on the Projected device.
|
|
116
|
+
*
|
|
117
|
+
* @param context any [Context] object. If the provided context is not a Projected device
|
|
118
|
+
* context, a Projected device context will be created automatically and used to create
|
|
119
|
+
* Projected [ActivityOptions].
|
|
120
|
+
* @throws IllegalStateException if the projected display was not found or if the Projected
|
|
121
|
+
* device doesn't have any displays.
|
|
122
|
+
*/
|
|
123
|
+
@JvmStatic
|
|
124
|
+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
|
125
|
+
public fun createProjectedActivityOptions(context: Context): ActivityOptions {
|
|
126
|
+
val projectedDeviceContext =
|
|
127
|
+
if (isProjectedDeviceContext(context)) {
|
|
128
|
+
context
|
|
129
|
+
} else {
|
|
130
|
+
createProjectedDeviceContext(context)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
val projectedDisplayIds = getProjectedDisplayIds(projectedDeviceContext)
|
|
134
|
+
|
|
135
|
+
check(projectedDisplayIds.isNotEmpty()) { "Projected device doesn't have any displays." }
|
|
136
|
+
|
|
137
|
+
val projectedDisplay =
|
|
138
|
+
context.getSystemService(DisplayManager::class.java).displays.find {
|
|
139
|
+
it.name == PROJECTED_DISPLAY_NAME && projectedDisplayIds.contains(it.displayId)
|
|
140
|
+
} ?: throw IllegalStateException("No projected display found.")
|
|
141
|
+
|
|
142
|
+
return ActivityOptions.makeBasic().setLaunchDisplayId(projectedDisplay.displayId)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Observe whether a Projected device is connected to the host.
|
|
147
|
+
*
|
|
148
|
+
* @param context The context used to access the [VirtualDeviceManager]. It can be any context
|
|
149
|
+
* object.
|
|
150
|
+
* @param coroutineContext The CoroutineContext that includes CoroutineDispatcher which is where
|
|
151
|
+
* the Projected device connectivity observer is executed on.
|
|
152
|
+
* @throws IllegalArgumentException if provided coroutineContext doesn't include
|
|
153
|
+
* CoroutineDispatcher.
|
|
154
|
+
*/
|
|
155
|
+
@JvmStatic
|
|
156
|
+
@RequiresApi(Build.VERSION_CODES.BAKLAVA)
|
|
157
|
+
public fun isProjectedDeviceConnected(
|
|
158
|
+
context: Context,
|
|
159
|
+
coroutineContext: CoroutineContext,
|
|
160
|
+
): Flow<Boolean> {
|
|
161
|
+
val hostContext = createHostDeviceContext(context)
|
|
162
|
+
return callbackFlow {
|
|
163
|
+
@OptIn(ExperimentalStdlibApi::class)
|
|
164
|
+
val coroutineDispatcher =
|
|
165
|
+
coroutineContext[CoroutineDispatcher]
|
|
166
|
+
?: throw IllegalArgumentException(
|
|
167
|
+
"CoroutineContext must contain a CoroutineDispatcher."
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
fun checkAndSend() {
|
|
171
|
+
trySend(isProjectedDisplayAvailable(hostContext))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
val virtualDeviceListener =
|
|
175
|
+
object : VirtualDeviceManager.VirtualDeviceListener {
|
|
176
|
+
override fun onVirtualDeviceCreated(deviceId: Int) {
|
|
177
|
+
checkAndSend()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
override fun onVirtualDeviceClosed(deviceId: Int) {
|
|
181
|
+
checkAndSend()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
val displayListener =
|
|
186
|
+
object : DisplayManager.DisplayListener {
|
|
187
|
+
override fun onDisplayAdded(displayId: Int) {
|
|
188
|
+
checkAndSend()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
override fun onDisplayChanged(displayId: Int) {
|
|
192
|
+
checkAndSend()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
override fun onDisplayRemoved(displayId: Int) {
|
|
196
|
+
checkAndSend()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
checkAndSend()
|
|
201
|
+
|
|
202
|
+
val virtualDeviceManager =
|
|
203
|
+
hostContext.getSystemService(VirtualDeviceManager::class.java)
|
|
204
|
+
virtualDeviceManager.registerVirtualDeviceListener(
|
|
205
|
+
coroutineDispatcher.asExecutor(),
|
|
206
|
+
virtualDeviceListener,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
val displayManager = hostContext.getSystemService(DisplayManager::class.java)
|
|
210
|
+
val eventFilter =
|
|
211
|
+
EVENT_TYPE_DISPLAY_ADDED or EVENT_TYPE_DISPLAY_CHANGED or EVENT_TYPE_DISPLAY_REMOVED
|
|
212
|
+
displayManager.registerDisplayListener(
|
|
213
|
+
coroutineDispatcher.asExecutor(),
|
|
214
|
+
eventFilter,
|
|
215
|
+
displayListener,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
awaitClose {
|
|
219
|
+
virtualDeviceManager.unregisterVirtualDeviceListener(virtualDeviceListener)
|
|
220
|
+
displayManager.unregisterDisplayListener(displayListener)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
.distinctUntilChanged()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
|
227
|
+
private fun isProjectedDisplayAvailable(context: Context): Boolean {
|
|
228
|
+
val projectedDeviceContext =
|
|
229
|
+
try {
|
|
230
|
+
createProjectedDeviceContext(context)
|
|
231
|
+
} catch (e: IllegalStateException) {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
val projectedDisplayIds = getProjectedDisplayIds(projectedDeviceContext)
|
|
235
|
+
if (projectedDisplayIds.isEmpty()) {
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
val displayManager = context.getSystemService(DisplayManager::class.java)
|
|
239
|
+
return displayManager.displays.any {
|
|
240
|
+
it.name == PROJECTED_DISPLAY_NAME && projectedDisplayIds.contains(it.displayId)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
245
|
+
private fun getProjectedDeviceId(context: Context) =
|
|
246
|
+
context
|
|
247
|
+
.getSystemService(VirtualDeviceManager::class.java)
|
|
248
|
+
.virtualDevices
|
|
249
|
+
// TODO: b/424824481 - Replace the name matching with a better method.
|
|
250
|
+
.find { it.name?.startsWith(PROJECTED_DEVICE_NAME) ?: false }
|
|
251
|
+
?.deviceId
|
|
252
|
+
|
|
253
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
254
|
+
private fun getVirtualDevice(context: Context) =
|
|
255
|
+
context.getSystemService(VirtualDeviceManager::class.java).virtualDevices.find {
|
|
256
|
+
it.deviceId == context.deviceId
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
|
260
|
+
private fun getProjectedDisplayIds(context: Context) =
|
|
261
|
+
getVirtualDevice(context)?.displayIds ?: IntArray(size = 0)
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
<br />
|
package/skills/android-display-glasses-with-jetpack-compose-glimmer/references/stack-source.md
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
When creating a Glimmer Stack component, refer to the following source code in
|
|
2
|
+
`Stack.kt`:
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
```kotlin
|
|
6
|
+
/*
|
|
7
|
+
* Copyright 2025 The Android Open Source Project
|
|
8
|
+
*
|
|
9
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
* you may not use this file except in compliance with the License.
|
|
11
|
+
* You may obtain a copy of the License at
|
|
12
|
+
*
|
|
13
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
*
|
|
15
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
* See the License for the specific language governing permissions and
|
|
19
|
+
* limitations under the License.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
package androidx.xr.glimmer.stack
|
|
23
|
+
|
|
24
|
+
import androidx.compose.animation.core.VisibilityThreshold
|
|
25
|
+
import androidx.compose.animation.core.spring
|
|
26
|
+
import androidx.compose.foundation.layout.PaddingValues
|
|
27
|
+
import androidx.compose.foundation.pager.PagerDefaults
|
|
28
|
+
import androidx.compose.foundation.pager.PagerSnapDistance
|
|
29
|
+
import androidx.compose.foundation.pager.PagerState
|
|
30
|
+
import androidx.compose.foundation.pager.VerticalPager
|
|
31
|
+
import androidx.compose.runtime.Composable
|
|
32
|
+
import androidx.compose.runtime.derivedStateOf
|
|
33
|
+
import androidx.compose.runtime.referentialEqualityPolicy
|
|
34
|
+
import androidx.compose.runtime.remember
|
|
35
|
+
import androidx.compose.runtime.rememberUpdatedState
|
|
36
|
+
import androidx.compose.ui.Modifier
|
|
37
|
+
import androidx.compose.ui.draw.drawWithContent
|
|
38
|
+
import androidx.compose.ui.focus.FocusRequester
|
|
39
|
+
import androidx.compose.ui.focus.focusRequester
|
|
40
|
+
import androidx.compose.ui.focus.onFocusChanged
|
|
41
|
+
import androidx.compose.ui.geometry.Offset
|
|
42
|
+
import androidx.compose.ui.geometry.Size
|
|
43
|
+
import androidx.compose.ui.graphics.BlendMode
|
|
44
|
+
import androidx.compose.ui.graphics.Color
|
|
45
|
+
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
46
|
+
import androidx.compose.ui.layout.Layout
|
|
47
|
+
import androidx.compose.ui.layout.onPlaced
|
|
48
|
+
import androidx.compose.ui.unit.dp
|
|
49
|
+
import androidx.compose.ui.util.fastFirstOrNull
|
|
50
|
+
import androidx.compose.ui.util.fastForEach
|
|
51
|
+
import androidx.compose.ui.util.fastMap
|
|
52
|
+
import androidx.compose.ui.util.lerp
|
|
53
|
+
import androidx.compose.ui.zIndex
|
|
54
|
+
import androidx.xr.glimmer.internal.SingleItemScrollConstraintConnection
|
|
55
|
+
import kotlin.math.roundToInt
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* [VerticalStack] is a lazy, vertically scrollable layout that arranges its items in a visually
|
|
59
|
+
* overlapping, three-dimensional sequence, which resembles a deck of cards. The primary item is
|
|
60
|
+
* prominently displayed in the foreground. Subsequent items are positioned behind the primary item
|
|
61
|
+
* along the z-axis with a small portion of the next item revealed to indicate depth and upcoming
|
|
62
|
+
* content.
|
|
63
|
+
*
|
|
64
|
+
* As the user scrolls vertically, the active foreground item transitions out of view, allowing the
|
|
65
|
+
* item immediately beneath it to slide into the prominent foreground position. Items always
|
|
66
|
+
* snap-animate into the foreground position after the user's gesture ends.
|
|
67
|
+
*
|
|
68
|
+
* Note: When displaying text within a [VerticalStack], it is strongly recommended to set
|
|
69
|
+
* [androidx.compose.ui.text.TextStyle.textMotion] to
|
|
70
|
+
* [androidx.compose.ui.text.style.TextMotion.Animated]. This ensures smooth rendering during layout
|
|
71
|
+
* animations or scaling transitions, preventing pixel-snapping artifacts.
|
|
72
|
+
*
|
|
73
|
+
* @sample androidx.xr.glimmer.samples.VerticalStackSample
|
|
74
|
+
* @param modifier the modifier to apply to this layout.
|
|
75
|
+
* @param state the state of the stack.
|
|
76
|
+
* @param content a block that describes the content. Inside this block you can use methods like
|
|
77
|
+
* [StackScope.item] to add a single item or [StackScope.items] to add a collection of items.
|
|
78
|
+
*/
|
|
79
|
+
@Composable
|
|
80
|
+
public fun VerticalStack(
|
|
81
|
+
modifier: Modifier = Modifier,
|
|
82
|
+
state: StackState = rememberStackState(),
|
|
83
|
+
content: StackScope.() -> Unit,
|
|
84
|
+
) {
|
|
85
|
+
val latestContent = rememberUpdatedState(content)
|
|
86
|
+
val stackItemHolderState =
|
|
87
|
+
remember(state) {
|
|
88
|
+
// Re-run the DSL to parse items only when the content lambda instance changes.
|
|
89
|
+
derivedStateOf(referentialEqualityPolicy()) {
|
|
90
|
+
StackItemHolder(state, latestContent.value).also {
|
|
91
|
+
// Set the item count on the StackState immediately when the derived state
|
|
92
|
+
// re-evaluates (i.e., when content changes), even before recomposition.
|
|
93
|
+
state.itemCount = it.itemCount
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
.also {
|
|
97
|
+
// This second assignment is necessary to force the derivedStateOf lambda above
|
|
98
|
+
// (which is executed lazily) to execute synchronously inside this block.
|
|
99
|
+
state.itemCount = it.value.itemCount
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
val singleItemScrollConstraintConnection =
|
|
104
|
+
remember(state.pagerState) { SingleItemScrollConstraintConnection(state.pagerState) }
|
|
105
|
+
|
|
106
|
+
VerticalPager(
|
|
107
|
+
state = state.pagerState,
|
|
108
|
+
modifier =
|
|
109
|
+
modifier
|
|
110
|
+
.then(StackInitialFocusElement(state))
|
|
111
|
+
.stackScrim()
|
|
112
|
+
.nestedScroll(singleItemScrollConstraintConnection),
|
|
113
|
+
contentPadding = PaddingValues(bottom = RevealAreaSize),
|
|
114
|
+
key = { page -> stackItemHolderState.value.getKey(page) },
|
|
115
|
+
beyondViewportPageCount = MaxNextVisibleItemCount,
|
|
116
|
+
flingBehavior =
|
|
117
|
+
PagerDefaults.flingBehavior(
|
|
118
|
+
state = state.pagerState,
|
|
119
|
+
pagerSnapDistance = PagerSnapDistance.atMost(1),
|
|
120
|
+
snapAnimationSpec = SnapAnimationSpec,
|
|
121
|
+
),
|
|
122
|
+
) { page ->
|
|
123
|
+
val stackItemHolder = stackItemHolderState.value
|
|
124
|
+
stackItemHolder.withInterval(page) { localIndex, itemInterval ->
|
|
125
|
+
val key =
|
|
126
|
+
itemInterval.getKeyOrDefault(globalIndex = page, localIntervalIndex = localIndex)
|
|
127
|
+
val itemScope = itemInterval.getOrCreateItemScope(key)
|
|
128
|
+
itemScope.index = page
|
|
129
|
+
StackItemLayout(page = page, state = state, itemScope = itemScope) {
|
|
130
|
+
itemInterval.item(itemScope, localIndex)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Composable
|
|
137
|
+
private fun StackItemLayout(
|
|
138
|
+
page: Int,
|
|
139
|
+
state: StackState,
|
|
140
|
+
itemScope: StackItemScopeImpl,
|
|
141
|
+
modifier: Modifier = Modifier,
|
|
142
|
+
content: @Composable () -> Unit,
|
|
143
|
+
) {
|
|
144
|
+
val focusRequester = remember { FocusRequester() }
|
|
145
|
+
Layout(
|
|
146
|
+
content = content,
|
|
147
|
+
modifier =
|
|
148
|
+
modifier
|
|
149
|
+
.zIndex(-page.toFloat())
|
|
150
|
+
.onPlaced { itemScope.coordinates = it }
|
|
151
|
+
.maskItemsBelow(state, itemScope)
|
|
152
|
+
.focusRequester(focusRequester)
|
|
153
|
+
.onFocusChanged { state.onItemFocusChanged(page, it) },
|
|
154
|
+
) { measurables, constraints ->
|
|
155
|
+
var maxWidth = 0
|
|
156
|
+
var maxHeight = 0
|
|
157
|
+
val placeables = measurables.fastMap {
|
|
158
|
+
it.measure(constraints).also { placeable ->
|
|
159
|
+
maxWidth = maxOf(maxWidth, placeable.width)
|
|
160
|
+
maxHeight = maxOf(maxHeight, placeable.height)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!isLookingAhead) {
|
|
165
|
+
state.layoutInfoInternal.updateMeasuredHeight(index = page, height = maxHeight)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
layout(maxWidth, maxHeight) {
|
|
169
|
+
placeables.fastForEach { placeable ->
|
|
170
|
+
placeable.placeRelativeWithLayer(x = 0, y = 0) {
|
|
171
|
+
val revealHeight = RevealAreaSize.roundToPx()
|
|
172
|
+
val topItem = state.topItem
|
|
173
|
+
when {
|
|
174
|
+
page.isTopItem(topItem = topItem) -> {
|
|
175
|
+
translationY =
|
|
176
|
+
state.topItemTranslationY(
|
|
177
|
+
revealHeight = revealHeight,
|
|
178
|
+
topItemHeight = placeable.height,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
state.notifyAutoFocus(page, focusRequester)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
page.isNextItem(topItem = topItem) -> {
|
|
185
|
+
translationY =
|
|
186
|
+
state.nextItemTranslationY(
|
|
187
|
+
revealHeight = revealHeight,
|
|
188
|
+
topItem = topItem,
|
|
189
|
+
nextItemHeight = placeable.height,
|
|
190
|
+
)
|
|
191
|
+
val scale = state.nextItemScale()
|
|
192
|
+
scaleX = scale
|
|
193
|
+
scaleY = scale
|
|
194
|
+
|
|
195
|
+
state.notifyAutoFocus(page, focusRequester)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
page.isNextNextItem(topItem = topItem) -> {
|
|
199
|
+
translationY =
|
|
200
|
+
state.nextNextItemTranslationY(
|
|
201
|
+
revealHeight = revealHeight,
|
|
202
|
+
topItem = topItem,
|
|
203
|
+
nextNextItemHeight = placeable.height,
|
|
204
|
+
)
|
|
205
|
+
scaleX = NextItemMinScale
|
|
206
|
+
scaleY = NextItemMinScale
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Masks the items following the current item (items that are below the current item on Z-axis) if
|
|
217
|
+
* the item's decoration fills the viewport width.
|
|
218
|
+
*/
|
|
219
|
+
private fun Modifier.maskItemsBelow(state: StackState, itemScope: StackItemScopeImpl): Modifier =
|
|
220
|
+
this.drawWithContent {
|
|
221
|
+
val viewportSize = state.layoutInfoInternal.viewportSize
|
|
222
|
+
val viewportWidth = viewportSize.width.toFloat()
|
|
223
|
+
|
|
224
|
+
if (itemScope.maskWidth >= (viewportWidth - DecorationWidthNoiseThresholdPx)) {
|
|
225
|
+
// Only apply the mask to the items below (Z-axis) if the mask fills the viewport width.
|
|
226
|
+
val viewportHeight = viewportSize.height.toFloat()
|
|
227
|
+
drawRect(
|
|
228
|
+
Color.Black,
|
|
229
|
+
blendMode = BlendMode.DstOut,
|
|
230
|
+
// The coordinate space here is for the item's layout, which changes position in the
|
|
231
|
+
// stack viewport depending on the scroll position. We need to deduct the viewport
|
|
232
|
+
// height to make the starting Y offset negative, so that the mask region extends
|
|
233
|
+
// upwards to the top of the stack viewport.
|
|
234
|
+
topLeft = Offset(x = 0f, y = itemScope.maskBottomY - viewportHeight),
|
|
235
|
+
size = Size(width = viewportWidth, height = viewportHeight),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
drawContent()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The translation Y of the top item in pixels, which is always equal to the snapped position
|
|
244
|
+
* offset.
|
|
245
|
+
*/
|
|
246
|
+
private fun StackState.topItemTranslationY(revealHeight: Int, topItemHeight: Int) =
|
|
247
|
+
calculateTopPositionOffset(topItemHeight, revealHeight)
|
|
248
|
+
|
|
249
|
+
/** The current translation Y of the next item in pixels. */
|
|
250
|
+
private fun StackState.nextItemTranslationY(
|
|
251
|
+
revealHeight: Int,
|
|
252
|
+
topItem: Int,
|
|
253
|
+
nextItemHeight: Int,
|
|
254
|
+
): Float {
|
|
255
|
+
val nextPageOffset = pagerState.pageOffset(topItem + 1)
|
|
256
|
+
val progress = topItemOffsetFraction
|
|
257
|
+
val topItemHeight = layoutInfoInternal.measuredTopItemHeight
|
|
258
|
+
|
|
259
|
+
val topPageTopPositionOffset = calculateTopPositionOffset(topItemHeight, revealHeight)
|
|
260
|
+
val nextPageTopPositionOffset = calculateTopPositionOffset(nextItemHeight, revealHeight)
|
|
261
|
+
|
|
262
|
+
val topItemBottom = topPageTopPositionOffset + topItemHeight
|
|
263
|
+
// startOffset is the initial offset of the next item when it's revealed below the top item.
|
|
264
|
+
val startOffset =
|
|
265
|
+
calculateInitialBehindPosition(
|
|
266
|
+
itemAboveBottom = topItemBottom + revealHeight,
|
|
267
|
+
itemBehindHeight = nextItemHeight,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
val currentOffset =
|
|
271
|
+
lerp(start = startOffset, stop = nextPageTopPositionOffset, fraction = progress)
|
|
272
|
+
|
|
273
|
+
return currentOffset - nextPageOffset
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** The current scale of the next item. */
|
|
277
|
+
private fun StackState.nextItemScale(): Float {
|
|
278
|
+
val progress = topItemOffsetFraction
|
|
279
|
+
return lerp(start = NextItemMinScale, stop = 1f, fraction = progress)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** The current translation Y of the next-next item in pixels. */
|
|
283
|
+
private fun StackState.nextNextItemTranslationY(
|
|
284
|
+
revealHeight: Int,
|
|
285
|
+
topItem: Int,
|
|
286
|
+
nextNextItemHeight: Int,
|
|
287
|
+
): Float {
|
|
288
|
+
val nextNextPageOffset = pagerState.pageOffset(topItem + 2)
|
|
289
|
+
val nextItemHeight = layoutInfoInternal.measuredNextItemHeight
|
|
290
|
+
|
|
291
|
+
// Calculate where the next item *would* be if it were currently the top item.
|
|
292
|
+
val nextItemTopPositionOffset = calculateTopPositionOffset(nextItemHeight, revealHeight)
|
|
293
|
+
val nextItemTopPositionBottom = nextItemTopPositionOffset + nextItemHeight
|
|
294
|
+
|
|
295
|
+
// Calculate the static target position the next-next item.
|
|
296
|
+
// We add revealHeight to nextItemBottom to ensure the item waits at the exact position
|
|
297
|
+
// where it will need to be when it transitions to being the next item.
|
|
298
|
+
val offset =
|
|
299
|
+
calculateInitialBehindPosition(
|
|
300
|
+
itemAboveBottom = nextItemTopPositionBottom + revealHeight,
|
|
301
|
+
itemBehindHeight = nextNextItemHeight,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
return offset - nextNextPageOffset
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Calculates the offset from the top of the viewport for an item of a given height for the item's
|
|
309
|
+
* top snapped position. Items are aligned to the bottom of the stack layout.
|
|
310
|
+
*/
|
|
311
|
+
private fun StackState.calculateTopPositionOffset(itemHeight: Int, revealHeight: Int): Float {
|
|
312
|
+
val viewportHeight = layoutInfoInternal.viewportSize.height
|
|
313
|
+
return (viewportHeight - itemHeight - revealHeight).coerceAtLeast(0).toFloat()
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Calculates the initial offset for an item when it is positioned behind the item above it. */
|
|
317
|
+
private fun calculateInitialBehindPosition(itemAboveBottom: Float, itemBehindHeight: Int): Float =
|
|
318
|
+
itemAboveBottom - itemBehindHeight * NextItemPositioningScale
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* The main axis offset of the item in pixels from the top of the viewport.
|
|
322
|
+
*
|
|
323
|
+
* If the page is in the visible list (inside the viewport), we return its exact offset. If it's not
|
|
324
|
+
* in the list but is composed (due to beyondViewportPageCount), we calculate where it *should* be
|
|
325
|
+
* based on the current page and offset fraction.
|
|
326
|
+
*
|
|
327
|
+
* Note: this method assumes that the Pager's layoutInfo is already available.
|
|
328
|
+
*/
|
|
329
|
+
private fun PagerState.pageOffset(page: Int): Int {
|
|
330
|
+
val layoutInfo = layoutInfo
|
|
331
|
+
|
|
332
|
+
// First check if the page is already visible.
|
|
333
|
+
val visiblePage = layoutInfo.visiblePagesInfo.fastFirstOrNull { it.index == page }
|
|
334
|
+
if (visiblePage != null) return visiblePage.offset
|
|
335
|
+
|
|
336
|
+
// Calculate the distance in "pages" from the current snap position.
|
|
337
|
+
// (page - currentPage) gives the index distance.
|
|
338
|
+
// (- currentPageOffsetFraction) accounts for the sub-page scroll.
|
|
339
|
+
val offsetFromCurrentPage = page - currentPage - currentPageOffsetFraction
|
|
340
|
+
val stride = layoutInfo.pageSize + layoutInfo.pageSpacing
|
|
341
|
+
return (offsetFromCurrentPage * stride).roundToInt()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** The size of the area where the items beneath the top of the stack item are revealed. */
|
|
345
|
+
internal val RevealAreaSize = 18.dp
|
|
346
|
+
|
|
347
|
+
/** The maximum number of items that can be visible at the same time in addition to the top item. */
|
|
348
|
+
private const val MaxNextVisibleItemCount = 2
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The scale of the next item when it is fully behind the top item, and the default scale of the
|
|
352
|
+
* item behind it (the next-next item).
|
|
353
|
+
*/
|
|
354
|
+
private const val NextItemMinScale = 0.94f
|
|
355
|
+
|
|
356
|
+
/** The scale factor that's between 1.0 and [NextItemMinScale], which is used in positioning. */
|
|
357
|
+
private const val NextItemPositioningScale = 0.97f // (1f + NextItemMinScale) / 2f
|
|
358
|
+
|
|
359
|
+
/** The animation spec used for snapping stack items. */
|
|
360
|
+
private val SnapAnimationSpec =
|
|
361
|
+
spring(
|
|
362
|
+
dampingRatio = 0.56f,
|
|
363
|
+
stiffness = 118f,
|
|
364
|
+
visibilityThreshold = Int.VisibilityThreshold.toFloat(),
|
|
365
|
+
)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
<br />
|