Tworzenie pierwszej aktywności na potrzeby okularów audio i okularów z wyświetlaczem

Obsługiwane urządzenia z XR
Ten przewodnik pomoże Ci tworzyć aplikacje na te typy urządzeń z XR.
Okulary z dźwiękiem i
wyświetlaczem

Rozszerzone funkcje okularów z dźwiękiem i wyświetlaczem są oparte na istniejącym interfejsie API frameworka Androida Activity i obejmują dodatkowe koncepcje, które obsługują unikalne aspekty tych okularów. W przeciwieństwie do zestawów słuchawkowych XR, które uruchamiają pełny plik APK na urządzeniu, okulary z dźwiękiem i wyświetlaczem używają specjalnej aktywności, która działa w ramach istniejącej aplikacji na telefonie. Ta aktywność jest wyświetlana z urządzenia hosta na okulary.

Aby utworzyć aplikację na okulary audio i okulary z wyświetlaczem, rozszerz istniejącą aplikację na telefon, tworząc nową aktywność Activity. Ta aktywność służy jako główny punkt uruchamiania aplikacji na okularach. Takie podejście upraszcza proces tworzenia, ponieważ możesz udostępniać i ponownie wykorzystywać logikę biznesową między aplikacjami na telefon i okulary.

Zgodność wersji

Sprawdź wymagania dotyczące zgodności pakietu Android SDK z pakietem Jetpack XR SDK.

Zależności

Dodaj te zależności biblioteki dla okularów audio i okularów z wyświetlaczem:

Dynamiczny

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

Zadeklaruj aktywność w pliku manifestu aplikacji

Podobnie jak w przypadku innych typów aktywności, musisz zadeklarować aktywność w pliku manifestu aplikacji, aby system mógł ją zobaczyć i uruchomić.

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

Najważniejsze informacje o kodzie

  • Określa android.hardware.display.category.XR_PROJECTED dla atrybutu android:requiredDisplayCategory, aby poinformować system, że jest to aktywność wyświetlana i może być wyświetlana na okularach z dźwiękiem i wyświetlaczem.
  • android.intent.action.MAIN ustawia tę aktywność jako domyślną aktywność uruchamiania.
  • android.intent.category.XR_PROJECTED_LAUNCHER to specjalna kategoria, która sprawia, że aktywność wyświetlana jest wykrywalna przez polecenia głosowe Gemini.

    Gdy użytkownik wyda polecenie głosowe, używając nazwy aplikacji (np. „Otwórz próbkę katalogu AI”, „Uruchom próbkę katalogu AI” lub „Uruchom próbkę katalogu AI”), system używa tej kategorii do zlokalizowania i uruchomienia wyznaczonej aktywności na okularach z wyświetlaczem.

Utwórz aktywność

Następnie utworzysz małą aktywność, która będzie mogła wyświetlać coś na okularach AI, gdy wyświetlacz jest włączony.

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

Najważniejsze informacje o kodzie

Zaimplementuj element typu „composable”

Utworzona aktywność odwołuje się do funkcji typu „composable” HomeScreen, którą musisz zaimplementować. Poniższy kod używa Jetpack Compose Glimmer do zdefiniowania elementu typu „composable”, który może wyświetlać tekst na wyświetlaczu okularów:

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

Najważniejsze informacje o kodzie

  • Jak zdefiniowano wcześniej w aktywności, funkcja HomeScreen zawiera treść typu „composable”, którą użytkownik widzi, gdy wyświetlacz okularów jest włączony.
  • Komponent Text Jetpack Compose Glimmer wyświetla tekst "Hello, AI Glasses!" na wyświetlaczu okularów.
  • Komponent Button Jetpack Compose Glimmer zamyka aktywność, wywołując finish() przez onClose w aktywności wyświetlanej.

Sprawdź, czy okulary z dźwiękiem lub wyświetlaczem są połączone

Aby sprawdzić, czy okulary z dźwiękiem lub okulary z wyświetlaczem użytkownika są połączone z ich telefonem przed uruchomieniem aktywności, użyj ProjectedContext.isProjectedDeviceConnected metody. Ta metoda zwraca Flow<Boolean>, którą aplikacja może obserwować, aby otrzymywać aktualizacje w czasie rzeczywistym o stanie połączenia.

Uruchom aktywność

Po utworzeniu podstawowej aktywności możesz ją uruchomić na okularach. Aby uzyskać dostęp do sprzętu okularów, aplikacja musi uruchomić aktywność z określonymi opcjami, które informują system o użyciu kontekstu wyświetlanego, jak pokazano w tym kodzie:

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

Metoda createProjectedActivityOptions w ProjectedContext generuje opcje niezbędne do uruchomienia aktywności w kontekście wyświetlanym. Parametr context może być kontekstem z telefonu lub okularów.

Dalsze kroki

Po utworzeniu pierwszej Activity na okulary audio i okulary z wyświetlaczem możesz poznać inne sposoby rozszerzania jej funkcjonalności: