投影されたコンテキストを使用して AI メガネのハードウェアにアクセスする

対象の XR デバイス
このガイダンスは、次のようなタイプの XR デバイス向けのエクスペリエンスを構築する際に役立ちます。
AI グラス

必要な権限をリクエストして付与された後、アプリは AI グラスのハードウェアにアクセスできます。スマートグラスのハードウェア(スマートフォンのハードウェアではなく)にアクセスするためのキーは、投影されたコンテキストを使用することです。

コードの実行場所に応じて、予測コンテキストを取得する主な方法は 2 つあります。

コードが AI グラス アクティビティで実行されている場合に、予測されるコンテキストを取得する

アプリのコードが AI グラスのアクティビティ内から実行されている場合、アプリ自身のアクティビティ コンテキストはすでに投影されたコンテキストです。このシナリオでは、その Activity 内で行われた呼び出しは、すでに Glass のハードウェアにアクセスできます。

スマートフォン アプリ コンポーネントで実行されているコードの投影コンテキストを取得する

AI グラス アクティビティ以外のアプリの一部(電話アクティビティやサービスなど)がグラスのハードウェアにアクセスする必要がある場合は、投影されたコンテキストを明示的に取得する必要があります。これを行うには、createProjectedDeviceContext() メソッドを使用します。

@OptIn(ExperimentalProjectedApi::class)
private fun getGlassesContext(context: Context): Context? {
    return try {
        // From a phone Activity or Service, get a context for the AI glasses.
        ProjectedContext.createProjectedDeviceContext(context)
    } catch (e: IllegalStateException) {
        Log.e(TAG, "Failed to create projected device context", e)
        null
    }
}

有効性を確認する

投影されたコンテキストを作成したら、ProjectedContext.isProjectedDeviceConnected をモニタリングします。このメソッドが true を返している間、投影されたコンテキストは接続されたデバイスに対して有効なままであり、AI グラスのハードウェアには、スマートフォンのアプリ アクティビティやサービス(CameraManager など)からアクセスできます。

切断時にクリーンアップする

投影されたコンテキストは接続されたデバイスのライフサイクルに関連付けられているため、デバイスが切断されると破棄されます。デバイスが切断されると、ProjectedContext.isProjectedDeviceConnectedfalse を返します。アプリはこの変更をリッスンし、その投影されたコンテキストを使用してアプリが作成したシステム サービス(CameraManager など)やリソースをクリーンアップする必要があります。

再接続時に再初期化する

AI グラス デバイスが再接続すると、アプリは createProjectedDeviceContext() を使用して別の投影コンテキスト インスタンスを取得し、新しい投影コンテキストを使用してシステム サービスまたはリソースを再初期化できます。

Bluetooth を使用して音声にアクセスする

現在、AI グラスは標準の Bluetooth オーディオ デバイスとしてスマートフォンに接続します。ヘッドセットと A2DP(Advanced Audio Distribution Profile)プロファイルの両方がサポートされています。このアプローチを使用すると、音声の入出力をサポートする Android アプリは、グラスをサポートする目的で構築されていなくても、グラスで動作します。場合によっては、投影されたコンテキストを使用してグラスのハードウェアにアクセスする代わりに、Bluetooth を使用する方がアプリのユースケースに適していることがあります。

標準の Bluetooth オーディオ機器と同様に、RECORD_AUDIO 権限を付与する権限は、メガネではなくスマートフォンによって制御されます。

AI グラスのカメラで画像をキャプチャする

AI グラスのカメラで画像をキャプチャするには、アプリの正しいコンテキストを使用して、CameraX の ImageCapture ユースケースをグラスのカメラに設定してバインドします。

private fun startCameraOnGlasses(activity: ComponentActivity) {
    // 1. Get the CameraProvider using the projected context.
    // When using the projected context, DEFAULT_BACK_CAMERA maps to the AI glasses' camera.
    val projectedContext = try {
        ProjectedContext.createProjectedDeviceContext(activity)
    } catch (e: IllegalStateException) {
        Log.e(TAG, "AI Glasses context could not be created", e)
        return
    }

    val cameraProviderFuture = ProcessCameraProvider.getInstance(projectedContext)

    cameraProviderFuture.addListener({
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

        // 2. Check for the presence of a camera.
        if (!cameraProvider.hasCamera(cameraSelector)) {
            Log.w(TAG, "The selected camera is not available.")
            return@addListener
        }

        // 3. Query supported streaming resolutions using Camera2 Interop.
        val cameraInfo = cameraProvider.getCameraInfo(cameraSelector)
        val camera2CameraInfo = Camera2CameraInfo.from(cameraInfo)
        val cameraCharacteristics = camera2CameraInfo.getCameraCharacteristic(
            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
        )

        // 4. Define the resolution strategy.
        val targetResolution = Size(1920, 1080)
        val resolutionStrategy = ResolutionStrategy(
            targetResolution,
            ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER
        )
        val resolutionSelector = ResolutionSelector.Builder()
            .setResolutionStrategy(resolutionStrategy)
            .build()

        // 5. If you have other continuous use cases bound, such as Preview or ImageAnalysis,
        // you can use  Camera2 Interop's CaptureRequestOptions to set the FPS
        val fpsRange = Range(30, 60)
        val captureRequestOptions = CaptureRequestOptions.Builder()
            .setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange)
            .build()

        // 6. Initialize the ImageCapture use case with options.
        val imageCapture = ImageCapture.Builder()
            // Optional: Configure resolution, format, etc.
            .setResolutionSelector(resolutionSelector)
            .build()

        try {
            // Unbind use cases before rebinding.
            cameraProvider.unbindAll()

            // Bind use cases to camera using the Activity as the LifecycleOwner.
            cameraProvider.bindToLifecycle(
                activity,
                cameraSelector,
                imageCapture
            )
        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }
    }, ContextCompat.getMainExecutor(activity))
}

コードに関する主なポイント

  • 投影されたデバイス コンテキストを使用して ProcessCameraProvider のインスタンスを取得します。
  • 投影されたコンテキストのスコープ内では、カメラを選択すると、AI グラスの主な外向きカメラが DEFAULT_BACK_CAMERA にマッピングされます。
  • 事前バインディング チェックでは、cameraProvider.hasCamera(cameraSelector) を使用して、選択したカメラがデバイスで使用可能であることを確認してから処理を進めます。
  • Camera2CameraInfoCamera2 Interop を使用して、基盤となる CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP を読み取ります。これは、サポートされている解像度に関する高度なチェックに役立ちます。
  • ImageCapture の出力画像解像度を正確に制御するために、カスタム ResolutionSelector が構築されています。
  • カスタム ResolutionSelector で構成された ImageCapture ユースケースを作成します。
  • ImageCapture ユースケースをアクティビティのライフサイクルにバインドします。これにより、アクティビティの状態に基づいてカメラの開閉が自動的に管理されます(たとえば、アクティビティが一時停止されたときにカメラが停止します)。

AI グラスのカメラを設定したら、CameraX の ImageCapture クラスを使用して画像をキャプチャできます。takePicture() を使用して画像をキャプチャする方法については、CameraX のドキュメントをご覧ください。

AI グラスのカメラで動画を撮影する

AI グラスのカメラで画像ではなく動画をキャプチャするには、ImageCapture コンポーネントを対応する VideoCapture コンポーネントに置き換え、キャプチャ実行ロジックを変更します。

主な変更点は、別のユースケースを使用すること、別の出力ファイルを作成すること、適切な動画録画方法を使用してキャプチャを開始することです。VideoCapture API とその使用方法について詳しくは、CameraX の動画キャプチャに関するドキュメントをご覧ください。

次の表に、アプリのユースケースに応じた推奨解像度とフレームレートを示します。

ユースケース 解像度 フレームレート
ビデオ通信 1280 x 720 15 FPS
コンピュータ ビジョン 640 x 480 10 FPS
AI 動画ストリーミング 640 x 480 1 FPS

AI グラス アクティビティからスマートフォンのハードウェアにアクセスする

AI グラス アクティビティは、createHostDeviceContext(context) を使用してホストデバイス(スマートフォン)のコンテキストを取得することで、スマートフォンのハードウェア(カメラやマイクなど)にアクセスすることもできます。

@OptIn(ExperimentalProjectedApi::class)
private fun getPhoneContext(activity: ComponentActivity): Context? {
    return try {
        // From an AI glasses Activity, get a context for the phone.
        ProjectedContext.createHostDeviceContext(activity)
    } catch (e: IllegalStateException) {
        Log.e(TAG, "Failed to create host device context", e)
        null
    }
}

ハイブリッド アプリ(モバイルと AI グラスの両方のエクスペリエンスを含むアプリ)でホストデバイス(スマートフォン)に固有のハードウェアまたはリソースにアクセスする場合は、アプリが正しいハードウェアにアクセスできるように、正しいコンテキストを明示的に選択する必要があります。

  • スマートフォンの Activity または ProjectedContext.createHostDeviceContext() から Activity コンテキストを使用して、スマートフォンのコンテキストを取得します。
  • getApplicationContext() は使用しないでください。メガネ アクティビティが最後に起動されたコンポーネントだった場合、アプリケーション コンテキストが AI グラスのコンテキストを誤って返す可能性があります。