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

Erweiterte Funktionen für Audio- und Displaybrillen basieren auf der vorhandenen Android-Activity-Framework-API und umfassen 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 Displaybrillen 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.

Um die App für Audio-Brillen und Display-Brillen zu optimieren, erweitern Sie Ihre vorhandene Telefon App, indem Sie ein neues projiziertes Activity erstellen. Diese Aktivität dient als Haupteinstiegspunkt für den Start Ihrer App auf der Brille. Dieser Ansatz vereinfacht die Entwicklung, da Sie Geschäftslogik zwischen Ihren Smartphone- und Brillen-Apps teilen und wiederverwenden können.

Versionskompatibilität

Kompatibilitätsanforderungen für das Android SDK für das Jetpack XR SDK

Abhängigkeiten

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

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

Aktivität im Manifest Ihrer App deklarieren

Wie bei anderen Arten von Aktivitäten 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 Displaybrillen projiziert werden kann.
  • Mit android.intent.action.MAIN wird diese Aktivität als Standard-Startaktivität festgelegt.
  • android.intent.category.XR_PROJECTED_LAUNCHER ist eine spezielle Kategorie, die dafür sorgt, dass Ihre geplanten Aktivitäten über Gemini-Sprachbefehle gefunden werden können.

    Wenn ein Nutzer einen Sprachbefehl mit dem Namen der App ausgibt (z. B. „Open the AI catalog sample“, „Launch the AI catalog sample“ oder „Start the AI catalog sample“), verwendet das System diese Kategorie, um die entsprechende Aktivität auf der Audio- oder Displaybrille 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 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))
    }
}

Wichtige Punkte zum Code

Composable implementieren

Die von Ihnen erstellte Aktivität verweist auf eine zusammensetzbare HomeScreen-Funktion, 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 Sie in Ihrer Aktivität zuvor definiert haben, enthält die Funktion HomeScreen die zusammensetzbaren Inhalte, die der Nutzer sieht, wenn das Display der Brille eingeschaltet ist.
  • Die Jetpack Compose-Komponente „Glimmer“ Text zeigt den Text „Hallo, KI-Brille!“ auf dem Display der Brille an.
  • Das Jetpack Compose-Glimmer Button schließt die Aktivität durch Aufrufen von finish() über onClose in der projizierten Aktivität.

Prüfen, ob eine Audio- oder Displaybrille verbunden ist

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

Aktivität starten

Nachdem Sie eine einfache Aktivität erstellt haben, können Sie sie auf Ihrer Brille starten. Damit Ihre App auf die Hardware der Brille zugreifen kann, muss sie Ihre Aktivität mit bestimmten Optionen starten, die dem System mitteilen, dass ein projizierter Kontext verwendet werden soll. Das folgende Beispiel zeigt, wie das geht:

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

Die Methode createProjectedActivityOptions 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 Ihre erste Activity für Audio-Brillen und Display-Brillen erstellt haben, können Sie die Funktionalität auf folgende Weise erweitern: