عرض الوسائط

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

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

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

ثلاثة أشكال عرض

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

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

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

العرض الحقيقي

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

استخدِم طريقة getMediaProjection() في خدمة تابعة لنظام التشغيل MediaProjectionManager لإنشاء مثيل MediaProjection عند بدء نشاط جديد. ابدأ النشاط باستخدام intent من الطريقة 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 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 التي تم طرحها في Android 11 (المستوى 30 لواجهة برمجة التطبيقات). (لمزيد من التفاصيل، يُرجى الاطّلاع على القسم حجم عرض الوسائط).

سطح

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

اعتبارًا من Android 12L (مستوى واجهة برمجة التطبيقات 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 لشاشة الجهاز حتى إذا كان تطبيق المضيف لعرض الوسائط في وضع النوافذ المتعددة، أي يشغل جزءًا فقط من الشاشة.

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

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

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

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

لضمان محاذاة عرض الوسائط بدقة مع حجم المحتوى الذي تم التقاطه في أي منطقة تم التقاطها وعند تدوير الجهاز، استخدِم معاودة الاتصال 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();

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

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

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

الشكل 2. شريحة شريط الحالة لمشاركة الشاشة وإرسال المحتوى وتسجيله

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

لضمان أنّ تطبيقك يحرّر الموارد ويعدّل واجهة المستخدم عند إيقاف عرض الشاشة من خلال تفاعل المستخدم مع شريحة شريط الحالة أو من خلال تفعيل شاشة القفل، اتّبِع الخطوات التالية:

  • أنشئ مثيلاً من MediaProjection.Callback.

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

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

مراجع إضافية

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