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

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

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

اطّلِع على نظرة عامة على الفواصل الإعلانية لمستقبل الويب للحصول على مزيد من المعلومات حول آلية عمل الفواصل الإعلانية.

على الرغم من أنّه يمكن تحديد الفواصل على كلّ من جهاز الإرسال وجهاز الاستقبال، إلا أنّه يُنصح تحديدها على 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);
        }
    }
}

تفعيل ميزة التشغيل التلقائي

اطّلِع على قسم واجهات برمجة التطبيقات للتشغيل التلقائي وإضافة المحتوى إلى "قائمة المحتوى التالي".

إلغاء اختيار الصور للتطبيقات المصغّرة لتجربة المستخدم

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

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

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

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

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

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

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

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

حالة "ما مِن أجهزة"

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

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

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

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