Les expériences augmentées pour les lunettes audio et les lunettes à écran sont basées sur l'API du framework Activity Android existante et incluent des concepts supplémentaires pour prendre en charge les aspects uniques de ces lunettes. Contrairement aux casques XR qui exécutent un APK complet sur l'appareil, les lunettes audio et les lunettes à écran utilisent une activité dédiée qui s'exécute dans l'application existante de votre téléphone. Cette activité est projetée de l'appareil hôte vers les lunettes.
Pour créer l'expérience de votre application pour les lunettes audio et les lunettes à écran, vous devez étendre votre application téléphonique existante en créant une nouvelle Activity projetée. Cette activité sert de point d'entrée principal pour le lancement de votre application sur les lunettes. Cette approche simplifie le développement, car vous pouvez partager et réutiliser la logique métier entre vos expériences sur téléphone et sur lunettes.
Compatibilité des versions
Consultez les exigences de compatibilité du SDK Android pour le SDK Jetpack XR.
Dépendances
Ajoutez les dépendances de bibliothèque suivantes pour les lunettes audio et les lunettes avec écran :
Groovy
dependencies {
implementation "androidx.xr.runtime:runtime:1.0.0-alpha15"
implementation "androidx.xr.glimmer:glimmer:1.0.0-alpha13"
implementation "androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha13"
implementation "androidx.xr.projected:projected:1.0.0-alpha08"
implementation "androidx.xr.arcore:arcore:1.0.0-alpha14"
}
Kotlin
dependencies {
implementation("androidx.xr.runtime:runtime:1.0.0-alpha15")
implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha13")
implementation("androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha13")
implementation("androidx.xr.projected:projected:1.0.0-alpha08")
implementation("androidx.xr.arcore:arcore:1.0.0-alpha14")
}
Déclarer votre activité dans le fichier manifeste de votre application
Comme pour les autres types d'activités, vous devez déclarer votre activité dans le fichier manifeste de votre application pour que le système puisse la voir et l'exécuter.
<application>
<activity
android:name="com.example.xr.projected.ProjectedMainActivity"
android:exported="true"
android:requiredDisplayCategory="android.hardware.display.category.XR_PROJECTED"
android:label="Example activity for audio glasses and display glasses">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.XR_PROJECTED_LAUNCHER"/>
</intent-filter>
</activity>
</application>
Points clés concernant le code
- Spécifie
android.hardware.display.category.XR_PROJECTEDpour l'attributandroid:requiredDisplayCategoryafin d'indiquer au système qu'il s'agit d'une activité projetée qui peut être projetée sur des lunettes audio et vidéo. android.intent.action.MAINdéfinit cette activité comme activité de lancement par défaut.android.intent.category.XR_PROJECTED_LAUNCHERest une catégorie spécialisée qui permet à Gemini de découvrir votre activité projetée à l'aide de commandes vocales.Lorsqu'un utilisateur émet une commande vocale en utilisant le nom de l'application (par exemple, "Ouvrir l'exemple de catalogue d'IA", "Lancer l'exemple de catalogue d'IA" ou "Démarrer l'exemple de catalogue d'IA"), le système utilise cette catégorie pour localiser et démarrer l'activité désignée sur les lunettes audio ou vidéo.
Créer votre activité
Ensuite, vous allez créer une petite activité qui peut afficher quelque chose sur les lunettes d'IA chaque fois que l'écran est allumé.
@OptIn(ExperimentalProjectedApi::class) class ProjectedMainActivity : ComponentActivity() { private var displayController: ProjectedDisplayController? = null private var isVisualUiSupported by mutableStateOf(false) private var areVisualsOn by mutableStateOf(true) private var isPermissionDenied by mutableStateOf(false) // Register the permissions launcher using the ProjectedPermissionsResultContract. private val requestPermissionLauncher: ActivityResultLauncher<List<ProjectedPermissionsRequestParams>> = registerForActivityResult(ProjectedPermissionsResultContract()) { results -> if (results[Manifest.permission.CAMERA] == true) { isPermissionDenied = false initializeGlassesFeatures() } else { // Handle permission denial. isPermissionDenied = true } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onDestroy(owner: LifecycleOwner) { displayController?.close() displayController = null } }) if (hasCameraPermission()) { initializeGlassesFeatures() } else { requestHardwarePermissions() } setContent { GlimmerTheme { HomeScreen( areVisualsOn = areVisualsOn, isVisualUiSupported = isVisualUiSupported, isPermissionDenied = isPermissionDenied, onRetryPermission = { requestHardwarePermissions() }, onClose = { finish() } ) } } } private fun initializeGlassesFeatures() { lifecycleScope.launch { // Check device capabilities val projectedDeviceController = ProjectedDeviceController.create(this@ProjectedMainActivity) isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI) val controller = ProjectedDisplayController.create(this@ProjectedMainActivity) displayController = controller val observer = GlassesLifecycleObserver( context = this@ProjectedMainActivity, controller = controller, onVisualsChanged = { visualsOn -> areVisualsOn = visualsOn } ) lifecycle.addObserver(observer) } } private fun hasCameraPermission(): Boolean { return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED } private fun requestHardwarePermissions() { val params = ProjectedPermissionsRequestParams( permissions = listOf(Manifest.permission.CAMERA), rationale = "Camera access is required to overlay digital content on your physical environment." ) requestPermissionLauncher.launch(listOf(params)) } }
Points clés concernant le code
- Active l'utilisation des API d'activation de la bibliothèque Jetpack Projected.
ProjectedMainActivityétendComponentActivity, comme vous vous y attendez dans le développement mobile.- Étant donné que toutes les lunettes ne sont pas équipées d'un écran, vérifiez si l'appareil en possède un à l'aide de
ProjectedDeviceController. - Le bloc
setContentde la fonctiononCreatedéfinit la racine de l'arborescence de l'UI composable pour l'activité. Vous allez implémenter le composableHomeScreenà l'aide de Jetpack Compose Glimmer. - Initialise l'UI pendant la méthode
onCreatede l'activité (voir cycle de vie de l'activité projetée). - Pour préparer les fonctionnalités liées à l'appareil photo qui accèdent au matériel des lunettes, demandez les autorisations matérielles en enregistrant un lanceur d'autorisations, en définissant les fonctions
hasCameraPermissionetrequestHardwarePermissions, et en vérifiant si les autorisations ont été accordées avant d'appelerinitializeGlassesFeatures.
Implémenter le composable
L'activité que vous avez créée fait référence à une fonction composable HomeScreen que vous devez implémenter. Le code suivant utilise Jetpack Compose Glimmer pour définir un composable qui peut afficher du texte sur l'écran des lunettes :
@Composable fun HomeScreen( areVisualsOn: Boolean, isVisualUiSupported: Boolean, isPermissionDenied: Boolean, onRetryPermission: () -> Unit, onClose: () -> Unit, modifier: Modifier = Modifier ) { Box( modifier = modifier .surface() .focusable(false) .fillMaxSize(), contentAlignment = Alignment.Center ) { if (isPermissionDenied) { Card( title = { Text("Permission Required") }, action = { Button(onClick = onClose) { Text("Exit") } } ) { Text("Camera access is needed to use AI glasses features.") Button(onClick = onRetryPermission) { Text("Retry") } } } else if (isVisualUiSupported) { Card( title = { Text("Android XR") }, action = { Button(onClick = onClose) { Text("Close") } } ) { if (areVisualsOn) { Text("Hello, AI Glasses!") } else { Text("Display is off. Audio guidance active.") } } } else { Text("Audio Guidance Mode Active") } } }
Points clés concernant le code
- Comme vous l'avez défini précédemment dans votre activité, la fonction
HomeScreeninclut le contenu composable que l'utilisateur voit lorsque l'écran des lunettes est allumé. - Le composant Glimmer
Textde Jetpack Compose affiche le texte "Hello, AI Glasses!" sur l'écran des lunettes. - Le Glimmer Jetpack Compose
Buttonferme l'activité en appelantfinish()viaonClosedans l'activité projetée.
Vérifier si des lunettes audio ou vidéo sont connectées
Pour déterminer si les lunettes audio ou l'écran d'un utilisateur sont connectés à son téléphone avant de lancer votre activité, utilisez la méthode ProjectedContext.isProjectedDeviceConnected. Cette méthode renvoie un Flow<Boolean> que votre application peut observer pour obtenir des informations en temps réel sur l'état de la connexion.
Démarrer votre activité
Maintenant que vous avez créé une activité de base, vous pouvez la lancer sur vos lunettes. Pour accéder au matériel des lunettes, votre application doit démarrer votre activité avec des options spécifiques qui indiquent au système d'utiliser un contexte projeté, comme indiqué dans le code suivant :
val options = ProjectedContext.createProjectedActivityOptions(context) val intent = Intent(context, ProjectedMainActivity::class.java) context.startActivity(intent, options.toBundle())
La méthode createProjectedActivityOptions dans ProjectedContext génère les options nécessaires pour démarrer votre activité dans un contexte projeté.
Le paramètre context peut être un contexte provenant du téléphone ou des lunettes.
Étapes suivantes
Maintenant que vous avez créé votre première activité pour les lunettes audio et les lunettes à écran, découvrez d'autres façons d'étendre ses fonctionnalités :
- Gérer la sortie audio à l'aide de Text-to-Speech
- Gérer les entrées audio à l'aide de la reconnaissance vocale automatique
- Créer une UI avec Jetpack Compose Glimmer
- Accéder au matériel des lunettes audio et des lunettes avec affichage