إضافة ميزات متقدّمة إلى تطبيق Android

الفواصل الإعلانية

توفّر حزمة Android Sender SDK إمكانية عرض الفواصل الإعلانية والإعلانات المصاحبة ضمن بث وسائط معيّن.

يمكنك الاطّلاع على نظرة عامة على الفواصل الإعلانية في Web Receiver لمزيد من المعلومات حول طريقة عمل الفواصل الإعلانية.

يمكن تحديد الفواصل الإعلانية على كلٍّ من جهاز الإرسال وجهاز الاستقبال، ولكن يُنصح بتحديدها على Web Receiver وAndroid TV Receiver للحفاظ على سلوك متّسق على جميع الأنظمة الأساسية.

على Android، حدِّد الفواصل الإعلانية في أمر تحميل باستخدام AdBreakClipInfo و AdBreakInfo:

Kotlin
val breakClip1: AdBreakClipInfo =
    AdBreakClipInfo.Builder("bc0")
        .setTitle("Clip title")
        .setPosterUrl("https://www.some.url")
        .setDuration(60000)
        .setWhenSkippableInMs(5000)  // Set this field so that the ad is skippable
        .build()

val breakClip2: AdBreakClipInfo = 
val breakClip3: AdBreakClipInfo = 

val break1: AdBreakClipInfo =
    AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000)
        .setId("b0")
        .setBreakClipIds({"bc0","bc1","bc2"})
        
        .build()

val mediaInfo: MediaInfo = MediaInfo.Builder()
    
    .setAdBreaks({break1})
    .setAdBreakClips({breakClip1, breakClip2, breakClip3})
    .build()

val mediaLoadRequestData: MediaLoadRequestData = MediaInfo.Builder()
    
    .setMediaInfo(mediaInfo)
    .build()

remoteMediaClient.load(mediaLoadRequestData)
Java
AdBreakClipInfo breakClip1 =
    new AdBreakClipInfo.Builder("bc0")
        .setTitle("Clip title")
        .setPosterUrl("https://www.some.url")
        .setDuration(60000)
        .setWhenSkippableInMs(5000)  // Set this field so that the ad is skippable
        .build();

AdBreakClipInfo breakClip2 = 
AdBreakClipInfo breakClip3 = 

AdBreakInfo break1 =
    new AdBreakInfo.Builder(/* playbackPositionInMs= */ 10000)
        .setId("b0")
        .setBreakClipIds({"bc0","bc1","bc2"})
        
        .build();

MediaInfo mediaInfo = new MediaInfo.Builder()
    
    .setAdBreaks({break1})
    .setAdBreakClips({breakClip1, breakClip2, breakClip3})
    .build();

MediaLoadRequestData mediaLoadRequestData = new MediaInfo.Builder()
    
    .setMediaInfo(mediaInfo)
    .build();

remoteMediaClient.load(mediaLoadRequestData);

إضافة إجراءات مخصّصة

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

// In AndroidManifest.xml
<receiver android:name="com.example.MyMediaIntentReceiver" />
Kotlin
// In your OptionsProvider
var mediaOptions = CastMediaOptions.Builder()
    .setMediaIntentReceiverClassName(MyMediaIntentReceiver::class.java.name)
    .build()

// Implementation of MyMediaIntentReceiver
internal class MyMediaIntentReceiver : MediaIntentReceiver() {
    override fun onReceiveActionTogglePlayback(currentSession: Session) {
    }

    override fun onReceiveActionMediaButton(currentSession: Session, intent: Intent) {
    }

    override fun onReceiveOtherAction(context: Context?, action: String, intent: Intent) {
    }
}
Java
// In your OptionsProvider
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setMediaIntentReceiverClassName(MyMediaIntentReceiver.class.getName())
        .build();

// Implementation of MyMediaIntentReceiver
class MyMediaIntentReceiver extends MediaIntentReceiver {
    @Override
    protected void onReceiveActionTogglePlayback(Session currentSession) {
    }

    @Override
    protected void onReceiveActionMediaButton(Session currentSession, Intent intent) {
    }

    @Override
    protected void onReceiveOtherAction(Context context, String action, Intent intent) {
    }
}

إضافة قناة مخصّصة

لكي يتمكّن تطبيق جهاز الإرسال من التواصل مع تطبيق جهاز الاستقبال، يجب أن ينشئ تطبيقك قناة مخصّصة. يمكن لجهاز الإرسال استخدام القناة المخصّصة لإرسال رسائل نصية إلى جهاز الاستقبال. يتم تحديد كل قناة مخصّصة من خلال مساحة اسم فريدة ويجب أن تبدأ بالبادئة urn:x-cast:، مثلاً، urn:x-cast:com.example.custom. من الممكن أن يكون لديك قنوات مخصّصة متعددة، لكل منها مساحة اسم فريدة. يمكن لتطبيق جهاز الاستقبال أيضًا إرسال الرسائل واستلامها باستخدام مساحة الاسم نفسها.

يتم تنفيذ القناة المخصّصة باستخدام الواجهة: Cast.MessageReceivedCallback

Kotlin
class HelloWorldChannel : MessageReceivedCallback {
    val namespace: String
        get() = "urn:x-cast:com.example.custom"

    override fun onMessageReceived(castDevice: CastDevice, namespace: String, message: String) {
        Log.d(TAG, "onMessageReceived: $message")
    }
}
Java
class HelloWorldChannel implements Cast.MessageReceivedCallback {
    public String getNamespace() {
        return "urn:x-cast:com.example.custom";
    }
    @Override
    public void onMessageReceived(CastDevice castDevice, String namespace, String message) {
        Log.d(TAG, "onMessageReceived: " + message);
    }
}

بعد ربط تطبيق جهاز الإرسال بتطبيق جهاز الاستقبال، يمكن إنشاء القناة المخصّصة باستخدام طريقة setMessageReceivedCallbacks:

Kotlin
try {
    mCastSession.setMessageReceivedCallbacks(
        mHelloWorldChannel.namespace,
        mHelloWorldChannel)
} catch (e: IOException) {
    Log.e(TAG, "Exception while creating channel", e)
}
Java
try {
    mCastSession.setMessageReceivedCallbacks(
            mHelloWorldChannel.getNamespace(),
            mHelloWorldChannel);
} catch (IOException e) {
    Log.e(TAG, "Exception while creating channel", e);
}

بعد إنشاء القناة المخصّصة، يمكن لجهاز الإرسال استخدام الـ sendMessage لإرسال رسائل نصية إلى جهاز الاستقبال عبر هذه القناة:

Kotlin
private fun sendMessage(message: String) {
    if (mHelloWorldChannel != null) {
        try {
            mCastSession.sendMessage(mHelloWorldChannel.namespace, message)
                .setResultCallback { status ->
                    if (!status.isSuccess) {
                        Log.e(TAG, "Sending message failed")
                    }
                }
        } catch (e: Exception) {
            Log.e(TAG, "Exception while sending message", e)
        }
    }
}
Java
private void sendMessage(String message) {
    if (mHelloWorldChannel != null) {
        try {
            mCastSession.sendMessage(mHelloWorldChannel.getNamespace(), message)
                .setResultCallback( status -> {
                    if (!status.isSuccess()) {
                        Log.e(TAG, "Sending message failed");
                    }
                });
        } catch (Exception e) {
            Log.e(TAG, "Exception while sending message", e);
        }
    }
}

السماح بالتشغيل التلقائي

يمكنك الاطّلاع على قسم واجهات برمجة التطبيقات للتشغيل التلقائي ووضع العناصر في قائمة الانتظار.

إلغاء اختيار الصور لعناصر واجهة المستخدم

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

تحدّد فئة ImagePicker وسيلة لاختيار صورة مناسبة من قائمة الصور في MediaMetadata، استنادًا إلى استخدام الصورة، مثلاً صورة مصغّرة للإشعار أو خلفية بملء الشاشة. تختار عملية التنفيذ التلقائية لـ ImagePicker الصورة الأولى دائمًا، أو تعرض قيمة فارغة إذا لم تتوفّر أي صورة في MediaMetadata. يمكن لتطبيقك إنشاء فئة فرعية من ImagePicker وإلغاء طريقة onPickImage(MediaMetadata, ImageHints) لتوفير عملية تنفيذ بديلة، ثم اختيار هذه الفئة الفرعية باستخدام طريقة setImagePicker في CastMediaOptions.Builder. ImageHints توفّر تلميحات إلى ImagePicker حول نوع الصورة وحجمها المطلوبَين لعرضها في واجهة المستخدم.

تخصيص مربّعات حوار البث

إدارة دورة حياة الجلسة

SessionManager هو المكان المركزي لإدارة دورة حياة الجلسة. SessionManager يستمع إلى التغييرات في حالة اختيار مسار MediaRouter لبدء الجلسات واستئنافها وإنهائها. عند اختيار مسار، سيُنشئ SessionManager كائن Session ويحاول بدءه أو استئنافه. عند إلغاء اختيار مسار، سيُنهي SessionManager الجلسة الحالية.

لذلك، لضمان إدارة SessionManager لدورات حياة الجلسات بشكلٍ صحيح، عليك التأكّد مما يلي:

بناءً على طريقة إنشاء مربّعات حوار البث، قد تحتاج إلى اتّخاذ إجراءات إضافية:

  • إذا أنشأت مربّعات حوار البث باستخدام MediaRouteChooserDialog و MediaRouteControllerDialog، ستعدّل هذه المربّعات تلقائيًا اختيار المسار في MediaRouter، لذا لن تحتاج إلى اتّخاذ أي إجراء.
  • إذا أعددت زر البث باستخدام CastButtonFactory.setUpMediaRouteButton(Context, Menu, int) أو CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton)، يتم إنشاء مربّعات الحوار فعليًا باستخدام MediaRouteChooserDialog وMediaRouteControllerDialog، لذا لن تحتاج إلى اتّخاذ أي إجراء أيضًا.
  • في الحالات الأخرى، ستنشئ مربّعات حوار بث مخصّصة، لذا عليك اتّباع التعليمات أعلاه لتعديل حالة اختيار المسار في MediaRouter.

حالة عدم توفّر أي أجهزة

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

إذا كنت تستخدم MediaRouteChooserDialog التلقائي، يتم التعامل مع حالة عدم توفّر أي أجهزة.

الخطوات التالية

بهذا نكون قد انتهينا من الميزات التي يمكنك إضافتها إلى تطبيق جهاز الإرسال على Android. يمكنك الآن إنشاء تطبيق جهاز إرسال لنظام أساسي آخر (iOS أو الويب)، أو إنشاء تطبيق Web Receiver.