عدسات الكاميرا وإمكاناتها

ملاحظة: تشير هذه الصفحة إلى حزمة Camera2. ننصحك باستخدام CameraX، ما لم يتطلّب تطبيقك ميزات محدّدة منخفضة المستوى من Camera2. تتوافق كل من CameraX وCamera2 مع الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث.

تحتوي العديد من أجهزة Android الحديثة على كاميرتَين أو أكثر في الجهة الأمامية أو الخلفية أو على كلا جانبَي الجهاز. يمكن أن تتمتع كل عدسة بقدرات فريدة، مثل الالتقاط المتتابع أو التحكم اليدوي أو تتبع الحركة. على سبيل المثال، قد يستخدم تطبيق إيداع الشيكات الكاميرا الخلفية الأولى فقط، بينما قد يستخدم تطبيق التواصل الاجتماعي الكاميرا الأمامية تلقائيًا، ولكنه يتيح للمستخدمين التبديل بين جميع العدسات المتاحة. ويمكنه أيضًا تذكُّر خياراتهم.

تتناول هذه الصفحة كيفية إدراج عدسات الكاميرا وإمكاناتها حتى تتمكّن من اتخاذ قرارات داخل تطبيقك بشأن العدسة التي يجب استخدامها في موقف معيّن. يسترد مقتطف الرمز التالي قائمة بجميع الكاميرات ويكررها:

try {
    val cameraIdList = cameraManager.cameraIdList // may be empty

    // iterate over available camera devices.
    for (cameraId in cameraIdList) {
        val characteristics = cameraManager.getCameraCharacteristics(cameraId)
        val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
        val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)

        // check if the selected camera device supports basic features
        // ensures backward compatibility with the original Camera API.
        val isBackwardCompatible = cameraCapabilities?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
        // ...
    }
} catch (e: CameraAccessException) {
    e.message?.let { Log.e(TAG, it) }
    // ...
}

يصف المتغيّر cameraLensFacing الاتجاه الذي تواجهه الكاميرا بالنسبة إلى شاشة الجهاز، وله إحدى القيم التالية:

لمزيد من المعلومات حول إعدادات اتجاه العدسة، يُرجى الاطّلاع على CameraCharacteristics.LENS_FACING.

يحتوي المتغيّر cameraCapabilities من عينة التعليمات البرمجية السابقة على معلومات حول الإمكانات المتنوعة، بما في ذلك ما إذا كانت الكاميرا قادرة على إنتاج إطارات عادية كمخرجات (على عكس بيانات جهاز استشعار العمق فقط، على سبيل المثال). يمكنك البحث عمّا إذا كان CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE من الإمكانات المدرَجة للكاميرا، والتي يتم تخزينها كعلامة في isBackwardCompatible.

اختيار الإعدادات التلقائية المناسبة

في تطبيقك، من المحتمل أنك تريد فتح كاميرا معيّنة تلقائيًا (إذا كانت متاحة). على سبيل المثال، من المرجح أن يفتح تطبيق السيلفي الكاميرا الأمامية، بينما قد يبدأ تطبيق الواقع المعزز بالكاميرا الخلفية. تعرض الدالة التالية الكاميرا الأولى التي تواجه اتجاهًا معيّنًا:

fun getFirstCameraIdFacing(
    cameraManager: CameraManager,
    facing: Int = CameraMetadata.LENS_FACING_BACK
): String? {
    try {
        // Get list of all compatible cameras.
        val cameraIds = cameraManager.cameraIdList.filter {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
            capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
            ) ?: false
        }

        // Iterate over the list of cameras and return the first one matching desired
        // lens-facing configuration.
        cameraIds.forEach {
            val characteristics = cameraManager.getCameraCharacteristics(it)
            if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
                return it
            }
        }

        // If no camera matched desired orientation, return the first one from the list.
        return cameraIds.firstOrNull()
    } catch (e: CameraAccessException) {
        e.message?.let { Log.e(TAG, it) }
        return null
    }
}

تفعيل التبديل بين الكاميرات

توفّر العديد من تطبيقات الكاميرا للمستخدمين خيار التبديل بين الكاميرات:

واجهة تطبيق "كاميرا Google" تعرض زر تبديل الكاميرا
الشكل 1: زر "تبديل الكاميرا" في تطبيق "كاميرا Google"

تحتوي العديد من الأجهزة على كاميرات متعددة تواجه الاتجاه نفسه. ويحتوي بعضها على كاميرات USB خارجية. لتزويد المستخدمين بواجهة مستخدم تتيح لهم التبديل بين الكاميرات الأمامية والخلفية، اختَر أول كاميرا متاحة لكل إعداد ممكن لجهة العدسة.

على الرغم من عدم وجود منطق عام لاختيار الكاميرا التالية، فإنّ الرمز البرمجي التالي يعمل مع معظم حالات الاستخدام:

fun filterCompatibleCameras(
    cameraIds: Array<String>,
    cameraManager: CameraManager
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
            CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
        ) ?: false
    }
}

fun filterCameraIdsFacing(
    cameraIds: List<String>,
    cameraManager: CameraManager,
    facing: Int
): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists.
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK
    )
    val frontCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT
    )
    val externalCameras = filterCameraIdsFacing(
        cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL
    )

    // The recommended order of iteration is: all external, first back, first front.
    val allCameras = (
        externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull()
        )
        ).filterNotNull()

    // Get the index of the currently selected camera in the list.
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be in the list, for example it could be an
    // external camera that has been removed by the user.
    return if (cameraIndex == -1) {
        // Return the first camera from the list.
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary.
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

يعمل هذا الرمز مع مجموعة كبيرة من الأجهزة ذات الإعدادات المختلفة. لمزيد من المعلومات حول احتساب الحالات الاستثنائية، يُرجى الاطّلاع على CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

إنشاء تطبيقات متوافقة

بالنسبة إلى التطبيقات التي لا تزال تستخدم واجهة Camera API المتوقفة نهائيًا، يعتمد عدد الكاميرات التي تعرضها Camera.getNumberOfCameras() على تنفيذ المصنّع الأصلي للجهاز. إذا كانت هناك كاميرا منطقية متعددة في النظام، للحفاظ على التوافق مع الأنظمة القديمة للتطبيق، ستعرض هذه الطريقة كاميرا واحدة فقط لكل كاميرا منطقية ومجموعة كاميرات فعلية أساسية. استخدِم واجهة برمجة التطبيقات Camera2 للاطّلاع على جميع الكاميرات.

لمزيد من المعلومات الأساسية حول اتجاهات الكاميرا، يُرجى الاطّلاع على Camera.CameraInfo.orientation.

بشكل عام، استخدِم واجهة برمجة التطبيقات Camera.getCameraInfo() لطلب البحث عن جميع orientation الكاميرا، وعرض كاميرا واحدة فقط لكل اتجاه متاح للمستخدمين الذين يبدّلون بين الكاميرات.

التوافق مع جميع أنواع الأجهزة

لا تفترض أنّ تطبيقك يعمل دائمًا على جهاز محمول مزوّد بكاميرا واحدة أو كاميرتَين. بدلاً من ذلك، اختَر الكاميرات الأكثر ملاءمةً للتطبيق. إذا لم تكن بحاجة إلى كاميرا معيّنة، اختَر أول كاميرا تواجه الاتجاه المحدّد. إذا كانت الكاميرا التي تواجه اتجاهًا معينًا غير متوفرة، ففكر فيما إذا كان بإمكان المستخدم إكمال رحلته بكاميرا مختلفة. لا تقيّد مدى توفّر تطبيقك على أجهزة معيّنة استنادًا إلى مكوّنات الكاميرا. إذا تم توصيل كاميرا خارجية، من المرجّح أن يفضّلها المستخدم ككاميرا تلقائية.