As experiências de realidade aumentada para óculos com áudio e óculos com tela virtual são criadas com base na API do framework Android Activity e incluem outros conceitos para oferecer suporte aos aspectos exclusivos desses óculos. Ao contrário dos headsets XR que executam um APK completo no dispositivo, os óculos com áudio e óculos com tela virtual usam uma Activity dedicada que é executada no app do smartphone. Essa atividade é projetada do dispositivo host para os óculos.
Para criar a experiência do app para óculos com áudio e óculos com tela virtual, você estende o app para smartphone atual criando uma nova Activity projetada. Essa atividade serve como o principal ponto de entrada de inicialização do app nos óculos. Essa abordagem simplifica o desenvolvimento porque você pode compartilhar e reutilizar a lógica de negócios entre as experiências do smartphone e dos óculos.
Compatibilidade de versões
Confira os requisitos de compatibilidade do SDK do Android para o SDK do Jetpack XR.
Dependências
Adicione as seguintes dependências de biblioteca para óculos de áudio e óculos de tela:
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")
}
Declare sua atividade no manifesto do app
Assim como outros tipos de atividades, é necessário declarar a sua no arquivo de manifesto do app para que o sistema a veja e a execute.
<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>
Pontos principais sobre o código
- Especifica
android.hardware.display.category.XR_PROJECTEDpara o atributoandroid:requiredDisplayCategorypara informar ao sistema que essa é uma atividade projetada e pode ser projetada em áudio e óculos com tela virtual. android.intent.action.MAINdefine essa atividade como a atividade de inicialização padrão.android.intent.category.XR_PROJECTED_LAUNCHERé uma categoria especializada que torna sua atividade projetada detectável por comandos de voz do Gemini.Quando um usuário emite um comando de voz usando o nome do app (por exemplo, "Abrir a amostra do catálogo de IA", "Iniciar a amostra do catálogo de IA" ou "Começar a amostra do catálogo de IA"), o sistema usa essa categoria para localizar e iniciar a atividade designada nos óculos de áudio ou nos óculos com tela virtual.
Criar sua atividade
Em seguida, você vai criar uma pequena atividade que pode mostrar algo nos óculos de IA sempre que a tela for ativada.
@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)) } }
Pontos principais sobre o código
- Ativa o uso de APIs de ativação da biblioteca Jetpack Projected.
- O
GlassesMainActivityestendeComponentActivity, como seria esperado no desenvolvimento para dispositivos móveis. - Como nem todos os óculos têm uma tela, verifique se o dispositivo tem uma
tela usando
ProjectedDeviceController. - O bloco
setContentna funçãoonCreatedefine a raiz da árvore de interface combinável da atividade. Você vai implementar o elemento combinávelHomeScreenusando o Jetpack Compose Glimmer. - Inicializa a UI durante o método
onCreateda atividade. Consulte o ciclo de vida da atividade projetada. - Para se preparar para recursos relacionados à câmera que acessam o hardware dos óculos, solicite permissões de hardware registrando um iniciador de permissões, definindo as funções
hasCameraPermissionerequestHardwarePermissionse verificando se as permissões foram concedidas antes de chamarinitializeGlassesFeatures.
Implementar o elemento combinável
A atividade criada faz referência a uma função combinável HomeScreen que
você precisa implementar. O código a seguir usa o Jetpack Compose Glimmer para
definir um elemento combinável que pode mostrar algum texto na tela dos óculos:
@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") } } }
Pontos principais sobre o código
- Como você definiu na atividade anterior, a função
HomeScreeninclui o conteúdo combinável que o usuário vê quando a tela dos óculos está ligada. - O componente
Textdo Glimmer do Jetpack Compose mostra o texto "Hello, AI Glasses!" na tela dos óculos. - O brilho
Buttondo Jetpack Compose fecha a atividade chamandofinish()poronClosena atividade projetada.
Verificar se os óculos de áudio ou de exibição estão conectados
Para determinar se os óculos de áudio ou os óculos com tela virtual de um usuário estão conectados ao smartphone dele antes de iniciar sua atividade, use o método ProjectedContext.isProjectedDeviceConnected. Esse método
retorna um Flow<Boolean> que seu app pode observar para receber atualizações em tempo real sobre
o status da conexão.
Começar a atividade
Agora que você criou uma atividade básica, é possível iniciá-la nos óculos. Para acessar o hardware dos óculos, o app precisa iniciar a atividade com opções específicas que informam ao sistema para usar um contexto projetado, conforme mostrado no código a seguir:
val options = ProjectedContext.createProjectedActivityOptions(context) val intent = Intent(context, GlassesMainActivity::class.java) context.startActivity(intent, options.toBundle())
O método createProjectedActivityOptions em ProjectedContext
gera as opções necessárias para iniciar sua atividade em um contexto projetado.
O parâmetro context pode ser um contexto do smartphone ou do dispositivo de óculos.
Próximas etapas
Agora que você criou sua primeira Activity para óculos com áudio e óculos com tela virtual, confira outras maneiras de estender a funcionalidade dela:
- Processar a saída de áudio usando o Text-to-Speech
- Processar entrada de áudio usando o reconhecimento de fala automático
- Criar interface com o Glimmer do Jetpack Compose
- Acessar hardware em óculos com áudio e óculos com tela virtual