Erste Aktivität für Audio- und Displaybrillen erstellen

XR‑Geräte, für die der Leitfaden gilt
Dieser Leitfaden hilft Ihnen dabei, Erlebnisse für die folgenden Arten von XR-Geräten zu entwickeln.
Audio- und
Display-Brillen

Erweiterte Erlebnisse für Audio- und Display-Brillen basieren auf der vorhandenen Android-ActivityFramework-API und enthalten zusätzliche Konzepte, um die besonderen Aspekte dieser Brillen zu unterstützen. Im Gegensatz zu XR‑Headsets, auf denen eine vollständige APK auf dem Gerät ausgeführt wird, verwenden Audio- und Display-Brillen eine spezielle Aktivität, die in der vorhandenen App auf Ihrem Smartphone ausgeführt wird. Diese Aktivität wird vom Hostgerät auf die Brille projiziert.

Wenn Sie das Erlebnis Ihrer App für Audio- und Display-Brillen erstellen möchten, erweitern Sie Ihre vorhandene Smartphone-App, indem Sie eine neue projizierte Activity. Diese Aktivität dient als Haupteinstiegspunkt für Ihre App auf der Brille. Dieser Ansatz vereinfacht die Entwicklung, da Sie Geschäftslogik zwischen Ihren Smartphone- und Brillen-Erlebnissen gemeinsam nutzen können.

Versionskompatibilität

Prüfen Sie die Anforderungen an die Android SDK-Kompatibilität für das Jetpack XR SDK.

Abhängigkeiten

Fügen Sie die folgenden Bibliotheksabhängigkeiten für Audio- und Display Brillen hinzu:

Groovy

dependencies {
    implementation "androidx.xr.runtime:runtime:1.0.0-alpha14"
    implementation "androidx.xr.glimmer:glimmer:1.0.0-alpha12"
    implementation "androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha12"
    implementation "androidx.xr.projected:projected:1.0.0-alpha07"
    implementation "androidx.xr.arcore:arcore:1.0.0-alpha13"
}

Kotlin

dependencies {
    implementation("androidx.xr.runtime:runtime:1.0.0-alpha14")
    implementation("androidx.xr.glimmer:glimmer:1.0.0-alpha12")
    implementation("androidx.xr.glimmer:glimmer-google-fonts:1.0.0-alpha12")
    implementation("androidx.xr.projected:projected:1.0.0-alpha07")
    implementation("androidx.xr.arcore:arcore:1.0.0-alpha13")
}

Aktivität im Manifest Ihrer App deklarieren

Wie bei anderen Aktivitätstypen müssen Sie Ihre Aktivität in der Manifestdatei Ihrer App deklarieren, damit das System sie sehen und ausführen kann.

<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>

Wichtige Punkte zum Code

  • Gibt android.hardware.display.category.XR_PROJECTED für das Attribut android:requiredDisplayCategory an, um dem System mitzuteilen, dass es sich um eine projizierte Aktivität handelt, die auf Audio- und Display-Brillen projiziert werden kann.
  • android.intent.action.MAIN legt diese Aktivität als Standard-Startaktivität fest.
  • android.intent.category.XR_PROJECTED_LAUNCHER ist eine spezielle Kategorie, mit der Ihre projizierte Aktivität über Gemini-Sprachbefehle gefunden werden kann.

    Wenn ein Nutzer einen Sprachbefehl mit dem Namen der App ausgibt (z. B. „Öffne das Beispiel für den KI-Katalog“, „Starte das Beispiel für den KI-Katalog“ oder „Beginne mit dem Beispiel für den KI-Katalog“), verwendet das System diese Kategorie, um die angegebene Aktivität auf der Audio- oder Display-Brille zu finden und zu starten.

Aktivität erstellen

Als Nächstes erstellen Sie eine kleine Aktivität, mit der etwas auf der KI-Brille angezeigt werden kann, wenn das Display eingeschaltet ist.

@OptIn(ExperimentalProjectedApi::class)
class GlassesMainActivity : 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@GlassesMainActivity)
            isVisualUiSupported = projectedDeviceController.capabilities.contains(CAPABILITY_VISUAL_UI)

            val controller = ProjectedDisplayController.create(this@GlassesMainActivity)
            displayController = controller
            val observer = GlassesLifecycleObserver(
                context = this@GlassesMainActivity,
                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))
    }
}

Wichtige Punkte zum Code

Zusammensetzbare Funktion implementieren

Die von Ihnen erstellte Aktivität verweist auf eine zusammensetzbare Funktion HomeScreen, die Sie implementieren müssen. Im folgenden Code wird Jetpack Compose Glimmer verwendet, um eine zusammensetzbare Funktion zu definieren, mit der Text auf dem Display der Brille angezeigt werden kann:

@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")
        }
    }
}

Wichtige Punkte zum Code

  • Wie bereits in Ihrer Aktivität definiert, enthält die Funktion HomeScreen den zusammensetzbaren Inhalt, den der Nutzer sieht, wenn das Display der Brille eingeschaltet ist.
  • Die Jetpack Compose Glimmer Text-Komponente zeigt den Text "Hallo, KI-Brille!" auf dem Display der Brille an.
  • Die Jetpack Compose Glimmer-Komponente Button schließt die Aktivität, indem sie finish() über onClose in der projizierten Aktivität aufruft.

Prüfen, ob eine Audio- oder Display-Brille verbunden ist

Mit der Methode ProjectedContext.isProjectedDeviceConnected können Sie prüfen, ob die Audio- oder Display-Brille eines Nutzers mit seinem Smartphone verbunden ist, bevor Sie Ihre Aktivität starten. Diese Methode gibt einen Flow<Boolean> zurück, den Ihre App beobachten kann, um Echtzeit-Updates zum Verbindungsstatus zu erhalten.

Aktivität starten

Nachdem Sie nun eine grundlegende Aktivität erstellt haben, können Sie sie auf Ihrer Brille starten. Um auf die Hardware der Brille zuzugreifen, muss Ihre App Ihre Aktivität mit bestimmten Optionen starten, die dem System mitteilen, dass ein projizierter Kontext verwendet werden soll. Das sehen Sie im folgenden Code:

val options = ProjectedContext.createProjectedActivityOptions(context)
val intent = Intent(context, GlassesMainActivity::class.java)
context.startActivity(intent, options.toBundle())

Die createProjectedActivityOptions Methode in ProjectedContext generiert die erforderlichen Optionen, um Ihre Aktivität in einem projizierten Kontext zu starten. Der Parameter context kann ein Kontext vom Smartphone oder von der Brille sein.

Nächste Schritte

Nachdem Sie nun Ihre erste Aktivität für Audio- und Display-Brillen erstellt haben, können Sie die Funktionalität auf verschiedene Weise erweitern: