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

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

توفّر حزمة تطوير البرامج (SDK) الخاصة بـ "أداة الإرسال" على Android إمكانية عرض فواصل إعلانية وإعلانات مصاحبة ضمن بث وسائط معيّن.

يمكنك الاطّلاع على نظرة عامة على فواصل الإعلانات في 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 بشأن نوع وحجم الصورة المطلوب اختيارها لعرضها في واجهة المستخدم.

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

إدارة مراحل نشاط الجلسة

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

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

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

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

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

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

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

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