Tạo hoạt động đầu tiên cho kính âm thanh và kính hiển thị

Các thiết bị XR được hỗ trợ
Hướng dẫn này giúp bạn xây dựng các trải nghiệm cho những loại thiết bị XR sau.
Kính âm thanh và
kính hiển thị

Trải nghiệm thực tế tăng cường cho kính âm thanh và kính hiển thị được xây dựng trên API khung ActivityAndroid hiện có 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ư thiết bị đeo đầu XR chạy một APK đầy đủ trên thiết bị, kính âm thanh và kính hiển thị 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. Hoạt động này được chiếu từ thiết bị lưu trữ sang kính.

Để tạo trải nghiệm ứng dụng cho kính âm thanh và kính hiển thị, bạn sẽ mở rộng ứng dụng Điện thoại hiện có bằng cách tạo một Activity được chiếu mới. Hoạt động này đóng vai trò là điểm truy cập khởi chạy chính cho ứ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 giữa các phiên bản

Kiểm tra các yêu cầu về khả năng tương thích của SDK Android đối với Jetpack XR SDK.

Phần phụ thuộc

Thêm các phần phụ thuộc thư viện sau đây cho kính âm thanh và kính hiển thị:

Groovy

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

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 của mình 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.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>

Các điểm chính về mã

  • Chỉ định xr_projected cho thuộc tính android:requiredDisplayCategory để cho hệ thống biết rằng hoạt động này nên sử dụng ngữ cảnh được chiếu để truy cập vào phần cứng từ một thiết bị thông minh.

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 nào đó trên kính AI bất cứ khi nào màn hình 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ã

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. Đoạn 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 HomeScreen bao gồm nội dung composable mà người dùng nhìn thấy khi màn hình của mắt kính đang bật.
  • Thành phần Jetpack Compose Glimmer Text hiển thị văn bản "Xin chào, Kính AI!" trên màn hình của kính.
  • Glimmer Button của Jetpack Compose đóng hoạt động bằng cách gọi finish() thông qua onClose trong hoạt động được chiếu.

Kiểm tra xem kính âm thanh hoặc kính màn hình đã được kết nối hay chưa

Để xác định xem kính âm thanh hoặc kính hiển thị của người dùng có kết nối với điện thoại của họ hay không trước khi chạy hoạt động, 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ể theo dõi để 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

Sau khi tạo một hoạt động cơ bản, bạn có thể khởi 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 khởi động hoạt động bằng các lựa chọn cụ thể để cho hệ thống biết rằng ứng dụng sẽ sử dụng ngữ cảnh được chiếu, như minh hoạ 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 sẽ tạo ra các lựa chọn cần thiết để bắt đầu hoạt động trong một bối cảnh được chiếu. Tham số context có thể là một ngữ cảnh từ điện thoại hoặc thiết bị kính.

Các bước tiếp theo

Giờ đây, bạn đã tạo Hoạt động đầu tiên cho kính âm thanh và kính hiển thị. Hãy khám phá những cách khác để mở rộng chức năng của Hoạt động này: