Trải nghiệm thực tế tăng cường cho kính phát âm thanh và kính có màn hình được xây dựng dựa trên
API khung Activity hiện có của Android và bao gồm các khái niệm
bổ sung để hỗ trợ các khía cạnh riêng biệt của những loại kính này. Không giống như tai nghe XR chạy toàn bộ APK trên thiết bị, kính phát âm thanh và kính có màn hình sử dụng một hoạt động chuyên dụng chạy trong ứng dụng hiện có trên điện thoại của bạn. Hoạt động này được chiếu từ thiết bị lưu trữ sang kính.
Để tạo trải nghiệm cho ứng dụng của bạn trên kính phát âm thanh và kính có màn hình, bạn hãy
mở rộng ứng dụng hiện có trên điện thoại bằng cách tạo một Activity mới được chiếu. Hoạt động này đóng vai trò là điểm truy cập chính để chạy ứng dụng của bạn trên kính. Phương pháp này giúp đơn giản hoá quá trình phát triển vì bạn có thể chia sẻ và sử dụng lại logic nghiệp vụ giữa trải nghiệm trên điện thoại và kính.
Khả năng tương thích với phiên bản
Hãy kiểm tra các yêu cầu về khả năng tương thích với SDK Android cho SDK XR Jetpack.
Phần phụ thuộc
Thêm các phần phụ thuộc thư viện sau đây cho kính phát âm thanh và kính có màn hình sau đây:
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")
}
Khai báo hoạt động trong tệp kê khai của ứng dụng
Giống như các loại hoạt động khác, bạn cần khai báo hoạt động trong tệp kê khai của ứng dụng để hệ thống có thể thấy và chạy hoạt động đó.
<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>
Các điểm chính về mã
- Chỉ định
android.hardware.display.category.XR_PROJECTEDcho thuộc tínhandroid:requiredDisplayCategoryđể cho hệ thống biết rằng đây là một hoạt động được chiếu và có thể được chiếu lên kính phát âm thanh và kính có màn hình. android.intent.action.MAINđặt hoạt động này làm hoạt động chạy mặc định.android.intent.category.XR_PROJECTED_LAUNCHERlà một danh mục chuyên biệt giúp các lệnh thoại của Gemini có thể phát hiện hoạt động được chiếu của bạn.Khi người dùng đưa ra một lệnh thoại bằng tên của ứng dụng (ví dụ: "Open the AI catalog sample" (Mở mẫu danh mục AI), "Launch the AI catalog sample" (Chạy mẫu danh mục AI) hoặc "Start the AI catalog sample" (Bắt đầu mẫu danh mục AI)), hệ thống sẽ sử dụng danh mục này để xác định vị trí và bắt đầu hoạt động được chỉ định trên kính phát âm thanh hoặc kính có màn hình.
Tạo hoạt động
Tiếp theo, bạn sẽ tạo một hoạt động nhỏ có thể hiển thị nội dung trên kính AI bất cứ khi nào màn hình được bật.
@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)) } }
Các điểm chính về mã
- Chọn sử dụng các API chọn tham gia từ thư viện Được chiếu của Jetpack.
GlassesMainActivitymở rộngComponentActivity, giống như bạn mong đợi trong quá trình phát triển ứng dụng di động.- Vì không phải loại mắt kính nào cũng có màn hình, hãy kiểm tra xem thiết bị có màn hình hay không bằng cách sử dụng
ProjectedDeviceController. - Khối
setContenttrong hàmonCreatexác định gốc của cây giao diện người dùng có khả năng kết hợp cho hoạt động. Bạn sẽ triển khai thành phần kết hợpHomeScreenbằng cách sử dụng Jetpack Compose Glimmer. - Khởi chạy giao diện người dùng trong phương thức
onCreatecủa activity (xem vòng đời của activity được chiếu). - Để chuẩn bị cho các tính năng liên quan đến camera mà
truy cập vào phần cứng của kính, yêu cầu quyền truy cập phần cứng bằng cách
đăng ký trình chạy quyền, xác định các hàm
hasCameraPermissionvàrequestHardwarePermissions, đồng thời kiểm tra xem quyền đã được cấp hay chưa trước khi gọiinitializeGlassesFeatures.
Triển khai thành phần kết hợp
Hoạt động mà bạn đã tạo tham chiếu đến một hàm composable HomeScreen mà bạn cần triển khai. Mã sau đây sử dụng Jetpack Compose Glimmer để
xác định một thành phần kết hợp có thể hiển thị một số văn bản trên màn hình của kính:
@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") } } }
Các điểm chính về mã
- Như bạn đã xác định trong hoạt động trước đó, hàm
HomeScreenbao gồm nội dung composable mà người dùng thấy khi màn hình của mắt kính bật. - Thành phần
Textcủa Jetpack Compose Glimmer hiển thị văn bản "Hello, AI Glasses!" trên màn hình của kính. Buttoncủa Jetpack Compose Glimmer đóng hoạt động bằng cách gọifinish()thông quaonClosetrong hoạt động được chiếu.
Kiểm tra xem kính phát âm thanh hoặc kính có màn hình đã được kết nối hay chưa
Để xác định xem kính phát âm thanh hoặc kính có màn hình của người dùng đã được kết nối với điện thoại của họ hay chưa trước khi chạy hoạt động của bạn, hãy sử dụng phương thức
ProjectedContext.isProjectedDeviceConnected. Phương thức này
trả về một Flow<Boolean> mà ứng dụng của bạn có thể quan sát để nhận thông tin cập nhật theo thời gian thực về
trạng thái kết nối.
Bắt đầu hoạt động
Bây giờ bạn đã tạo một hoạt động cơ bản, bạn có thể chạy hoạt động đó trên kính. Để truy cập vào phần cứng của kính, ứng dụng của bạn phải bắt đầu hoạt động bằng các tuỳ chọn cụ thể cho hệ thống biết để sử dụng ngữ cảnh được chiếu, như trong mã sau:
val options = ProjectedContext.createProjectedActivityOptions(context) val intent = Intent(context, GlassesMainActivity::class.java) context.startActivity(intent, options.toBundle())
Phương thức createProjectedActivityOptions trong ProjectedContext
tạo các tuỳ chọn cần thiết để bắt đầu hoạt động của bạn trong ngữ cảnh được chiếu.
Tham số context có thể là ngữ cảnh từ điện thoại hoặc thiết bị kính.
Các bước tiếp theo
Bây giờ bạn đã tạo hoạt động đầu tiên cho kính phát âm thanh và kính có màn hình, hãy khám phá các cách khác để mở rộng chức năng của hoạt động đó:
- Xử lý dữ liệu đầu ra âm thanh bằng tính năng Chuyển văn bản sang lời nói
- Xử lý dữ liệu đầu vào âm thanh bằng tính năng Nhận dạng lời nói tự động
- Tạo giao diện người dùng bằng Jetpack Compose Glimmer
- Truy cập vào phần cứng trên kính phát âm thanh và kính có màn hình