必要な権限をリクエストして付与された後、アプリは AI グラスのハードウェアにアクセスできます。スマートグラスのハードウェア(スマートフォンのハードウェアではなく)にアクセスするためのキーは、投影されたコンテキストを使用することです。
コードの実行場所に応じて、予測コンテキストを取得する主な方法は 2 つあります。
コードが AI グラス アクティビティで実行されている場合に、予測コンテキストを取得する
アプリのコードが AI グラスのアクティビティ内から実行されている場合、アプリ自身のアクティビティ コンテキストはすでに投影されたコンテキストです。このシナリオでは、そのアクティビティ内で行われた呼び出しは、すでにメガネのハードウェアにアクセスできます。
コードがスマートフォン アプリ コンポーネントで実行されている場合に、投影されたコンテキストを取得する
AI グラス アクティビティ以外のアプリの一部(スマートフォンのアクティビティやサービスなど)がグラスのハードウェアにアクセスする必要がある場合は、投影されたコンテキストを明示的に取得する必要があります。これを行うには、createProjectedDeviceContext() メソッドを使用します。
// From a phone Activity, get a context for the AI glasses
try {
val glassesContext = ProjectedContext.createProjectedDeviceContext(this)
// Now use glassesContext to access glasses' system services
} catch (e: IllegalStateException) {
// Projected device was not found
}
有効性を確認する
投影されたコンテキストを作成したら、ProjectedContext.isProjectedDeviceConnected をモニタリングします。このメソッドが true を返している間、投影されたコンテキストは接続されたデバイスに対して有効なままであり、スマートグラスのハードウェアには、スマートフォンのアプリ アクティビティやサービス(CameraManager など)からアクセスできます。
切断時にクリーンアップする
投影されたコンテキストは接続されたデバイスのライフサイクルに関連付けられているため、デバイスが切断されると破棄されます。デバイスが切断されると、ProjectedContext.isProjectedDeviceConnected は false を返します。アプリはこの変更をリッスンし、その投影されたコンテキストを使用してアプリが作成したシステム サービス(CameraManager など)やリソースをクリーンアップする必要があります。
再接続時に再初期化する
AI グラス デバイスが再接続すると、アプリは createProjectedDeviceContext() を使用して別の投影コンテキスト インスタンスを取得し、新しい投影コンテキストを使用してシステム サービスまたはリソースを再初期化できます。
Bluetooth を使用して音声にアクセスする
現在、AI グラスは標準の Bluetooth オーディオ デバイスとしてスマートフォンに接続します。ヘッドセットと A2DP(Advanced Audio Distribution Profile)プロファイルの両方がサポートされています。このアプローチを使用すると、音声の入出力をサポートする Android アプリは、メガネをサポートするよう意図的に構築されていなくても、メガネで動作します。場合によっては、投影されたコンテキストを使用してメガネのハードウェアにアクセスする代わりに、Bluetooth を使用する方がアプリのユースケースに適していることがあります。
標準の Bluetooth オーディオ デバイスと同様に、RECORD_AUDIO 権限を付与する権限は、メガネではなくスマートフォンによって制御されます。
AI グラスのカメラで画像をキャプチャする
AI グラスのカメラで画像をキャプチャするには、アプリの正しいコンテキストを使用して、CameraX の ImageCapture ユースケースをグラスのカメラに設定してバインドします。
private fun startCamera() {
// Get the CameraProvider using the projected context.
val cameraProviderFuture = ProcessCameraProvider.getInstance(
ProjectedContext.createProjectedDeviceContext(this)
)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Select the camera. When using the projected context, DEFAULT_BACK_CAMERA maps to the AI glasses' camera.
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// Check for the presence of a camera before initializing the ImageCapture use case.
if (!cameraProvider.hasCamera(cameraSelector)) {
Log.w(TAG, "The selected camera is not available.")
return@addListener
}
// Get supported streaming resolutions.
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector)
val camera2CameraInfo = Camera2CameraInfo.from(cameraInfo)
val cameraCharacteristics = camera2CameraInfo.getCameraCharacteristic(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
// 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()
// 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, 30)
val captureRequestOptions = CaptureRequestOptions.Builder()
.setCaptureRequestOption(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,fpsRange)
.build()
// Initialize the ImageCapture use case.
val imageCapture = ImageCapture.Builder()
// Optional: Configure resolution, format, etc.
.setResolutionSelector(resolutionSelector)
.build()
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// 4. Bind use cases to camera
cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageCapture)
} catch(exc: Exception) {
// This catches exceptions like IllegalStateException if use case binding fails
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
コードに関する主なポイント
- 投影されたデバイス コンテキストを使用して
ProcessCameraProviderのインスタンスを取得します。 - 投影されたコンテキストのスコープ内では、カメラを選択すると、AI グラスの主な外向きカメラが
DEFAULT_BACK_CAMERAにマッピングされます。 - 事前バインディング チェックでは、
cameraProvider.hasCamera(cameraSelector)を使用して、選択したカメラがデバイスで使用可能であることを確認してから処理を進めます。 Camera2CameraInfoで Camera2 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) を使用してホストデバイス(スマートフォン)のコンテキストを取得することで、スマートフォンのハードウェア(カメラやマイクなど)にアクセスすることもできます。
// From an AI glasses Activity, get a context for the phone
val phoneContext = ProjectedContext.createHostDeviceContext(this)
// Now use phoneContext to access the phone's hardware
ハイブリッド アプリ(モバイルと AI グラスの両方のエクスペリエンスを含むアプリ)でホストデバイス(スマートフォン)に固有のハードウェアまたはリソースにアクセスする場合は、アプリが正しいハードウェアにアクセスできるように、正しいコンテキストを明示的に選択する必要があります。
- スマートフォンの
ActivityまたはProjectedContext.createHostDeviceContext()からActivityコンテキストを使用して、スマートフォンのコンテキストを取得します。 getApplicationContext()は使用しないでください。メガネ アクティビティが最後に起動されたコンポーネントだった場合、アプリのコンテキストが AI メガネのコンテキストを誤って返す可能性があります。