הוספה של תכונות מתקדמות לאפליקציית Android

הפסקות למודעות

ערכת ה-SDK של Android Sender תומכת בהפסקות למודעות ובמודעות נלוות בתוך סטרימינג נתון של מדיה.

בסקירה הכללית על הפסקות למודעות ב-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);
}

אחרי שיוצרים את הערוץ בהתאמה אישית, השולח יכול להשתמש ב-method‏ 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);
        }
    }
}

תמיכה בהפעלה אוטומטית

עיינו בקטע ממשקי API להפעלה אוטומטית ולהוספה לתור.

שינוי בחירת התמונות לווידג'טים של חוויית המשתמש (UX)

רכיבים שונים של המסגרת (כלומר תיבת הדו-שיח של Cast, הבקר המיניאטורי וה-UIMediaController, אם הם מוגדרים כך) יציגו גרפיקה של המדיה שמועברת כרגע. כתובות ה-URL של הגרפיקה של התמונה בדרך כלל כלולות ב-MediaMetadata של המדיה, אבל יכול להיות שלאפליקציית השליחה יש מקור חלופי לכתובות ה-URL.

הכיתה ImagePicker מגדירה אמצעי לבחירת תמונה מתאימה מרשימת התמונות ב-MediaMetadata, על סמך השימוש בתמונה, למשל, תמונה ממוזערת של התראה או רקע במסך מלא. ההטמעה של ImagePicker כברירת מחדל תמיד בוחרת את התמונה הראשונה, או מחזירה ערך null אם אין תמונה זמינה ב-MediaMetadata. האפליקציה יכולה ליצור מחלקת משנה של ImagePicker ולבטל את השיטה onPickImage(MediaMetadata, ImageHints) כדי לספק הטמעה חלופית, ואז לבחור את מחלקת המשנה הזו באמצעות השיטה setImagePicker של CastMediaOptions.Builder. ImageHints מספק רמזים ל-ImagePicker לגבי הסוג והגודל של התמונה שצריך לבחור כדי להציג בממשק המשתמש.

התאמה אישית של תיבות הדו-שיח של Cast

ניהול מחזור החיים של סשן

‫SessionManager הוא המקום המרכזי לניהול מחזור החיים של הסשן. ‫SessionManager מקשיב לשינויים במצב בחירת המסלול של Android MediaRouter כדי להתחיל, להמשיך ולסיים סשנים. כשבוחרים מסלול, SessionManager יוצר אובייקט Session ומנסה להפעיל או להמשיך אותו. כשמבטלים את הבחירה במסלול, SessionManager מסיים את הסשן הנוכחי.

לכן, כדי לוודא ש-SessionManager מנהל את מחזורי החיים של הסשנים כמו שצריך, אתם צריכים לוודא ש:

בהתאם לאופן שבו אתם יוצרים את תיבות הדו-שיח של Cast, יכול להיות שתצטרכו לבצע פעולות נוספות:

  • אם אתם יוצרים תיבות דו-שיח של Cast באמצעות MediaRouteChooserDialog ו- MediaRouteControllerDialog, תיבות הדו-שיח האלה יעדכנו את בחירת הנתיב ב-MediaRouter באופן אוטומטי, ולא תצטרכו לעשות שום דבר.
  • אם הגדרתם את הכפתור להפעלת Cast באמצעות CastButtonFactory.setUpMediaRouteButton(Context, Menu, int) או CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton), תיבות הדו-שיח נוצרות בפועל באמצעות MediaRouteChooserDialog ו-MediaRouteControllerDialog, כך שגם במקרה הזה אין צורך לעשות דבר.
  • במקרים אחרים, תצטרכו ליצור תיבות דו-שיח מותאמות אישית של Cast, ולכן תצטרכו לפעול לפי ההוראות שלמעלה כדי לעדכן את מצב בחירת המסלול ב-MediaRouter.

מצב של אפס מכשירים

אם אתם יוצרים תיבות דו-שיח מותאמות אישית של Cast, ה-MediaRouteChooserDialog המותאם אישית צריך לטפל כראוי במקרה שבו לא נמצאו מכשירים. בתיבת הדו-שיח צריכים להיות אינדיקטורים שמבהירים למשתמשים מתי האפליקציה עדיין מנסה למצוא מכשירים ומתי ניסיון הגילוי כבר לא פעיל.

אם אתם משתמשים ב-MediaRouteChooserDialog שמוגדר כברירת מחדל, המצב של אפס מכשירים כבר מטופל.

השלבים הבאים

אלה כל התכונות שאפשר להוסיף לאפליקציית השליחה ל-Android. עכשיו אתם יכולים ליצור אפליקציית שליחה לפלטפורמה אחרת (iOS או אינטרנט), או ליצור אפליקציית Web Receiver.