見出し画像

Androidのカメラにアクセスできるか判定する - REALITY Advent Calendar #10

REALITYの一つの機能にアバターカメラという機能があります。この機能は、アプリケーション中央下部のボタンを押してアバターを表示する画面の、右下のカメラのアイコンから使うことができます。

画像1

アバターカメラでは背景となる写真を撮る機能がUnityで実装されています。この画面に遷移する前の画面では、フロントカメラでユーザーの顔を検出してそれに合わせてアバターを描画しており、カメラの映像からトラッキングのデータにするところまでをAndroid Nativeで実装されています。そのような設計でどんな問題が起きたかというと、UnityがAndroidのカメラにアクセスしたタイミングで、前の画面でAndroid Nativeが使っていたカメラがreleaseされておらず、画面が真っ暗になることがありました。

この問題を解決するためにカメラAvailabilityかを判定するインターフェイスをUnity-Android Native間に追加しました。今回は具体的にどのようにAvailabilityを検出したかについて紹介します。

その瞬間にAndroidのカメラにアクセスできるかを取得することができるAPIが見当たらなかったので、カメラのAvailabilityが変わったことを検出できるCameraManager.AvailabilityCallbackを使いました。早速ですが出来上がったコードがこちら

class CameraAvailabilityReceiver(private val activity: FragmentActivity) {
   private var frontCameraAvailable = false
   private var backCameraAvailable = false

   private val cameraManager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
   private val frontCameraId = cameraManager.cameraIdList.firstOrNull {
       val facing = cameraManager.getCameraCharacteristics(it).get(CameraCharacteristics.LENS_FACING)
       facing == CameraCharacteristics.LENS_FACING_FRONT
   }

   private val backCameraId = cameraManager.cameraIdList.firstOrNull {
       val facing = cameraManager.getCameraCharacteristics(it).get(CameraCharacteristics.LENS_FACING)
       facing == CameraCharacteristics.LENS_FACING_BACK
   }

   init {
       init()
   }

   private fun init() {
       if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
           return
       }
       cameraManager.registerAvailabilityCallback(
           object : CameraManager.AvailabilityCallback() {
               override fun onCameraAvailable(cameraId: String) {
                   if (cameraId == frontCameraId) {
                       frontCameraAvailable = true
                   }
                   if (cameraId == backCameraId) {
                       backCameraAvailable = true
                   }
                   super.onCameraAvailable(cameraId)
               }

               override fun onCameraUnavailable(cameraId: String) {
                   if (cameraId == frontCameraId) {
                       frontCameraAvailable = false
                   }
                   if (cameraId == backCameraId) {
                       backCameraAvailable = false
                   }
                   super.onCameraAvailable(cameraId)
               }
           },
           null
       )
   }

   fun getAvailability(): Pair<Boolean, Boolean> = Pair(frontCameraAvailable, backCameraAvailable)
}

このクラスを使えば、下記のように値が取れます

val receiver = CameraAvailabilityReceiver(activity)
val (frontCameraAvailable, backCameraAvailable) = receiver.getAvailability()

カメラのパーミッション、両面にカメラがない機種への対応など考えるべきことが他にもありますが、おおよそ上記のような形で動くと思います。

明日の11日目はメインはiOS担当ですがサーバーもAndroidもなんでもできる増住さんにによる「REALITYにアーカイブ機能を!」です!