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

Odpowiednie urządzenia XR
Te wskazówki pomogą Ci tworzyć rozwiązania na te typy urządzeń XR.
Okulary audio i
wyświetlacz

Rozszerzone funkcje okularów audio i okularów z wyświetlaczem są oparte na istniejącym Activityinterfejsie API platformy Androidobejmują dodatkowe koncepcje, które uwzględniają unikalne aspekty tych okularów. W przeciwieństwie do gogli XR, które uruchamiają na urządzeniu pełny plik APK, okulary audio i okulary wyświetlające korzystają z dedykowanej aktywności, która działa w ramach istniejącej aplikacji na telefonie. Ta aktywność jest wyświetlana z urządzenia hosta na okularach.

Aby utworzyć aplikację na okulary audio i okulary z wyświetlaczem, rozszerz istniejącą aplikację na telefon, tworząc nowy projektowany Activity. Ta aktywność jest głównym punktem wejścia do uruchamiania aplikacji na okularach. Takie podejście upraszcza proces tworzenia aplikacji, ponieważ możesz udostępniać i ponownie wykorzystywać logikę biznesową w aplikacjach na telefon i okulary.

Zgodność wersji

Sprawdź wymagania dotyczące zgodności pakietu Android SDK w przypadku pakietu 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-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")
}

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.GlassesMainActivity"
      android:exported="true"
      android:requiredDisplayCategory="xr_projected"
      android:label="Example activity for audio glasses and display glasses">
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
      </intent-filter>
  </activity>
</application>

Najważniejsze informacje o kodzie

  • Określa xr_projected dla atrybutu android:requiredDisplayCategory, aby poinformować system, że ta aktywność powinna używać prognozowanego kontekstu do uzyskiwania dostępu do sprzętu z połączonego urządzenia.

Tworzenie aktywności

Następnie utworzysz małą aktywność, która będzie wyświetlać coś na okularach z AI, gdy tylko ekran będzie włączony.

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

Najważniejsze informacje o kodzie

Implementowanie funkcji typu „composable”

Utworzone działanie odwołuje się do funkcji HomeScreen typu „composable”, którą musisz wdrożyć. Poniższy kod korzysta z Jetpack Compose Glimmer, aby zdefiniować funkcję kompozycyjną, która 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

  • Zgodnie z definicją podaną wcześniej w aktywności funkcja HomeScreen obejmuje treści, które użytkownik widzi, gdy wyświetlacz okularów jest włączony.
  • Komponent Jetpack Compose Glimmer Text wyświetla na wyświetlaczu okularów tekst „Hello, AI Glasses!”.
  • Komponent Glimmer Jetpack Compose Button zamyka aktywność, wywołując finish() przez onClose w przewidywanej aktywności.

Sprawdzanie, czy okulary audio lub wyświetlające są połączone

Aby przed uruchomieniem aktywności sprawdzić, czy słuchawki lub okulary wyświetlające użytkownika są połączone z telefonem, użyj metody ProjectedContext.isProjectedDeviceConnected. Ta metoda zwraca Flow<Boolean>, które aplikacja może obserwować, aby otrzymywać aktualizacje stanu połączenia w czasie rzeczywistym.

Rozpocznij 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 prognozowanego kontekstu, jak pokazano w tym kodzie:

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

Metoda createProjectedActivityOptionsProjectedContext generuje opcje niezbędne do rozpoczęcia aktywności w przewidywanym kontekście. Parametr context może być kontekstem z telefonu lub okularów.

Dalsze kroki

Po utworzeniu pierwszej aktywności dla okularów audio i okularów z wyświetlaczem możesz poznać inne sposoby rozszerzania ich funkcjonalności: