為智慧眼鏡和螢幕眼鏡建立第一個活動

適用的 XR 裝置
這份指南可協助您為這類 XR 裝置打造體驗。
音訊和螢幕眼鏡

音訊眼鏡和螢幕眼鏡的擴增體驗是以現有的 Android Activity 架構 API 為基礎建構,並納入其他概念,支援這類眼鏡的獨特功能。與在裝置上執行完整 APK 的 XR 頭戴式裝置不同,音訊眼鏡和顯示眼鏡會使用專屬活動,在手機現有的應用程式中執行。這項活動會從主機裝置投影到眼鏡。

如要為語音眼鏡和顯示型眼鏡建立應用程式體驗,請建立新的投影Activity,藉此擴充現有的手機應用程式。這個 Activity 會做為眼鏡上應用程式的主要啟動進入點。這種做法可簡化開發作業,因為您可以在手機和眼鏡體驗之間共用及重複使用商業邏輯。

版本相容性

請參閱 Jetpack XR SDK 的Android SDK 相容性需求

依附元件

新增以下音訊眼鏡和螢幕眼鏡的程式庫依附元件

Groovy

dependencies {
    implementation "androidx.xr.runtime:runtime:1.0.0-alpha14"
    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-alpha14")
    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")
}

在應用程式資訊清單中宣告活動

與其他類型的活動一樣,您需要在應用程式的資訊清單檔案中宣告活動,系統才能查看及運作執行活動。

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

程式碼重點

  • 指定 android.hardware.display.category.XR_PROJECTED 屬性的 android:requiredDisplayCategory,告知系統這是投影活動,可投影至音訊和顯示型眼鏡。
  • android.intent.action.MAIN 會將這項活動設為預設啟動活動。
  • android.intent.category.XR_PROJECTED_LAUNCHER 是一個專門類別,可讓 Gemini 語音指令探索你預計進行的活動。

    當使用者透過應用程式名稱發出語音指令 (例如「開啟 AI 目錄範例」、「啟動 AI 目錄範例」或「開始 AI 目錄範例」) 時,系統會使用這個類別,在音訊或顯示型眼鏡上尋找並啟動指定活動。

建立活動

接著,您會建立小型活動,在螢幕開啟時,於 AI 眼鏡上顯示內容。

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

程式碼重點

實作可組合函式

您建立的活動會參照需要實作的 HomeScreen 可組合函式。下列程式碼使用 Jetpack Compose Glimmer 定義可組合函式,在眼鏡螢幕上顯示部分文字:

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

程式碼重點

  • 如您先前在活動中定義,HomeScreen 函式包含使用者在眼鏡螢幕開啟時看到的可組合內容。
  • Jetpack Compose Glimmer Text 元件會在眼鏡螢幕上顯示「Hello, AI Glasses!」文字。
  • Jetpack Compose Glimmer Button 會透過投影活動中的 onClose 呼叫 finish(),關閉活動。

確認音訊或顯示型眼鏡是否已連線

如要判斷使用者的音訊或顯示型眼鏡是否已連線至手機,請在啟動活動前使用 ProjectedContext.isProjectedDeviceConnected 方法。這個方法會傳回 Flow<Boolean>,應用程式可觀察這個物件,即時取得連線狀態的更新資訊。

開始活動

您已建立基本活動,現在可以將其啟動到眼鏡上。如要存取眼鏡的硬體,應用程式必須使用特定選項啟動活動,告知系統使用投影環境,如下列程式碼所示:

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

ProjectedContext 中的 createProjectedActivityOptions 方法會產生必要選項,在投影環境中啟動活動。context 參數可以是手機或眼鏡裝置的環境。

後續步驟

您已為語音眼鏡和顯示型眼鏡建立第一個 Activity,現在可以探索其他擴充功能的方法: