عرض الوسائط

تتيح لك واجهات برمجة التطبيقات android.media.projection التي تم تقديمها في Android 5 (المستوى 21 من واجهة برمجة التطبيقات) تسجيل محتويات شاشة الجهاز كبث وسائط يمكنك تشغيله أو تسجيله أو بثه على أجهزة أخرى، مثل أجهزة التلفزيون.

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

تضمن ميزة "مشاركة شاشة التطبيق" خصوصية المستخدم وتزيد من إنتاجيته، كما تُحسِّن من إنجاز المهام المتعددة من خلال السماح للمستخدمين بتشغيل تطبيقات متعددة مع حصر مشاركة المحتوى بتطبيق واحد فقط.

ثلاثة طرق لعرض الإعلانات

تلتقط ميزة "إلقاء الوسائط" محتوى شاشة الجهاز أو نافذة التطبيق، ثم تصوّر الصورة التي تم التقاطها على شاشة افتراضية تعرض الصورة على Surface.

شاشة الجهاز الفعلية معروضة على شاشة افتراضية محتوى
              الشاشة الافتراضية المكتوبة في Surface الذي يقدّمه التطبيق
الشكل 1. شاشة الجهاز أو نافذة التطبيق الفعلية التي يتم عرضها على الشاشة الافتراضية شاشة افتراضية مكتوبة في Surface الذي يوفّره التطبيق

يقدّم التطبيق Surface باستخدام MediaRecorder أو SurfaceTexture أو ImageReader، ما يستخدِم محتويات الشاشة التي تم التقاطها ويتيح لك إدارة الصور المعروضة على Surface في الوقت الفعلي. يمكنك حفظ الصور كتسجيل أو بثها على التلفزيون أو جهاز آخر.

العرض الفعلي

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

استخدِم طريقة getMediaProjection() لخدمة النظام MediaProjectionManager لإنشاء مثيل MediaProjection عند بدء نشاط جديد. ابدأ النشاط باستخدام نية من createScreenCaptureIntent() لتحديد عملية التقاط شاشة:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection

val startMediaProjection = registerForActivityResult(
    StartActivityForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        mediaProjection = mediaProjectionManager
            .getMediaProjection(result.resultCode, result.data!!)
    }
}

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];

ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult(
    new StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            mediaProjection[0] = mediaProjectionManager
                .getMediaProjection(result.getResultCode(), result.getData());
        }
    }
);

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

الشاشة الافتراضية

العنصر الرئيسي في عرض الوسائط هو الشاشة الافتراضية التي يمكنك إنشاؤها من خلال استدعاء createVirtualDisplay() في مثيل MediaProjection:

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

تحدّد المَعلمتان width وheight سمات الشاشة الافتراضية. للحصول على قيم العرض والارتفاع، استخدِم واجهات برمجة التطبيقات WindowMetrics التي تم تقديمها في الإصدار 11 من Android (المستوى 30 لواجهة برمجة التطبيقات). (لمعرفة التفاصيل، يُرجى الاطّلاع على القسم حجم عرض الوسائط).

السطح

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

اعتبارًا من الإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات)، عند عرض المحتوى الذي تم التقاطه على سطح العرض، يُكبِّر النظام المحتوى بشكلٍ موحّد مع الحفاظ على نسبة العرض إلى الارتفاع، بحيث تكون أبعاد المحتوى (العرض والارتفاع) مساوية أو أقل من الأبعاد المقابلة لسطح العرض. ويتم بعد ذلك تمركز المحتوى الذي تم التقاطه على السطح.

يُحسِّن نهج التكبير/التصغير في Android 12L ميزة بث الشاشة على أجهزة التلفزيون وغيرها من الشاشات الكبيرة من خلال زيادة حجم الصورة على السطح إلى أقصى حد مع ضمان الحفاظ على نسبة العرض إلى الارتفاع المناسبة.

إذن الوصول إلى الخدمة التي تعمل في المقدّمة

إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android أو إصدارًا أحدث، يجب أن يتضمّن ملف بيان التطبيق بيانًا للإذن الخاص بنوع الخدمة التي تعمل في المقدّمة mediaProjection:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

ابدأ خدمة عرض الوسائط من خلال مكالمة إلى startForeground().

إذا لم تحدّد نوع الخدمة التي تعمل في المقدّمة في الطلب، سيتم تلقائيًا ضبط النوع على عدد صحيح ثنائي لأنواع الخدمات التي تعمل في المقدّمة المحدّدة في البيان. إذا لم يحدِّد البيان أي أنواع خدمات، يُرسِل النظام سوى MissingForegroundServiceTypeException.

يجب أن يطلب تطبيقك موافقة المستخدم قبل كل جلسة لعرض الوسائط. الجلسة هي مكالمة واحدة إلى createVirtualDisplay(). يجب استخدام رمز MediaProjection مرّة واحدة فقط لإجراء المكالمة.

في الإصدار 14 من Android أو الإصدارات الأحدث، تُعرِض الطريقة createVirtualDisplay() خطأ SecurityException إذا كان تطبيقك ينفّذ أيًا مما يلي:

  • تمرير مثيل Intent تم إرجاعه من createScreenCaptureIntent() إلى getMediaProjection() أكثر من مرة
  • طلبات الاتصال بـ createVirtualDisplay() أكثر من مرة في MediaProjection المثيل نفسه

حجم عرض الوسائط

يمكن أن تلتقط ميزة "عرض الوسائط" شاشة الجهاز بالكامل أو نافذة تطبيق بغض النظر عن وضع النوافذ.

الحجم الأولي

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

استخدِم طريقة المنصة WindowManager getMaximumWindowMetrics() لعرض عنصر WindowMetrics على screen الجهاز حتى إذا كان تطبيق مضيف عرض الوسائط في وضع المنافذ المتعددة، ولا يشغل سوى جزء من الشاشة.

للتوافق مع الإصدار 14 من واجهة برمجة التطبيقات أو الإصدارات الأقدم، استخدِم الطريقة WindowMetricsCalculator computeMaximumWindowMetrics() من مكتبة Jetpack WindowManager.

استخدِم طريقة WindowMetrics getBounds() للحصول على عرض شاشة الجهاز وارتفاعها.

تغييرات الحجم

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

لضمان محاذاة عرض الوسائط بدقة مع حجم المحتوى الذي تم التقاطه لأي منطقة تم التقاطها وعلى مستوى عمليات تدوير الجهاز، استخدِم callback onCapturedContentResize() لتغيير حجم المحتوى الذي تم التقاطه. (لمزيد من المعلومات، يُرجى الاطّلاع على قسم التخصيص التالي).

التخصيص

يمكن لتطبيقك تخصيص تجربة المستخدم في عرض الوسائط باستخدام واجهات برمجة التطبيقات التالية MediaProjection.Callback:

  • onCapturedContentVisibilityChanged(): يتيح للتطبيق المضيف (التطبيق الذي بدأ عرض الوسائط) عرض المحتوى المشترَك أو إخفائه.

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

  • onCapturedContentResize(): يتيح للتطبيق المضيف تغيير حجم عرض الوسائط على الشاشة الافتراضية وعرض الوسائط Surface استنادًا إلى حجم منطقة الشاشة التي تم التقاطها.

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

استرداد الموارد

يجب أن يسجِّل تطبيقك طلب الاستدعاء MediaProjection onStop() للحصول على إشعار عند إيقاف جلسة عرض الوسائط وأصبح غير صالح. عند إيقاف الجلسة، من المفترض أن يُفرِج تطبيقك عن الموارد التي يحتفظ بها، مثل الشاشة الافتراضية وسطح العرض. إذا توقفت جلسة تصويب الوسائط، لن تتمكّن من إنشاء شاشة افتراضية جديدة، حتى إذا لم يسبق لتطبيقك إنشاء شاشة افتراضية لجلسة تصويب الوسائط هذه.

يستدعي النظام دالة الاستدعاء عند انتهاء عرض الوسائط. يمكن أن يحدث هذا الإيقاف لعدة أسباب، مثل:

  • يوقف المستخدم الجلسة باستخدام واجهة مستخدم التطبيق أو شريحة شريط حالة عرض الوسائط في النظام.
  • يتم قفل الشاشة
  • بدء جلسة عرض وسائط أخرى
  • إيقاف عملية التطبيق نهائيًا

إذا لم يسجِّل تطبيقك طلب إعادة الاتصال، سيؤدي أي طلب إلى createVirtualDisplay() IllegalStateException.

إيقاف

يفعّل نظام التشغيل Android 14 أو الإصدارات الأحدث ميزة مشاركة شاشة التطبيق تلقائيًا. توفّر كل جلسة تصويب وسائط للمستخدمين خيار مشاركة نافذة تطبيق أو الشاشة بالكامل.

يمكن لتطبيقك إيقاف مشاركة شاشة التطبيق من خلال استدعاء الأسلوب createScreenCaptureIntent(MediaProjectionConfig) مع وسيطة MediaProjectionConfig يتم إرجاعها من طلب إلى createConfigForDefaultDisplay().

إنّ استدعاء createScreenCaptureIntent(MediaProjectionConfig) مع وسيطة MediaProjectionConfig التي تم إرجاعها من استدعاء createConfigForUserChoice() هو مماثل للسلوك التلقائي، أي استدعاء createScreenCaptureIntent().

التطبيقات التي يمكن تغيير حجمها

احرص دائمًا على أن تكون تطبيقات عرض الوسائط قابلة لتغيير الحجم (resizeableActivity="true"). تتيح التطبيقات التي يمكن تغيير حجمها تغييرات في إعدادات الجهاز ووضع "النوافذ المتعددة" (راجِع دعم وضع "النوافذ المتعددة").

إذا لم يكن بإمكانك تغيير حجم تطبيقك، يجب أن يطلب التطبيق حدود الشاشة من سياق النافذة ويستخدم getMaximumWindowMetrics() لاسترداد WindowMetrics الحد الأقصى لمساحة العرض المتاحة للتطبيق :

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

شريحة شريط الحالة والإيقاف التلقائي

تؤدي إساءة استخدام ميزة "إلقاء الشاشة" إلى تعريض بيانات المستخدمين الخاصة، مثل معلوماتهم المالية، لأنّ المستخدمين لا يدركون أنّه تتم مشاركة شاشة أجهزتهم.

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

في الإصدار Android 15 QPR1 والإصدارات الأحدث، يتوقف عرض الشاشة تلقائيًا عند قفل شاشة الجهاز.

الشكل 2. شريحة في شريط الحالة لمشاركة الشاشة وبث الشاشة وتسجيل الشاشة

مصادر إضافية

لمزيد من المعلومات عن عرض الوسائط، يُرجى الاطّلاع على مقالة تسجيل تشغيل الفيديو والصوت.