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

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

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

למידע נוסף על אופן הפעולה של הפסקות למודעות, ניתן לעיין במאמר סקירה כללית של הפסקות למודעות אצל מקבלי אינטרנט.

אמנם אפשר לציין הפסקות גם אצל השולח וגם אצל המקבל, אבל מומלץ לציין אותן ב-Web זוגr וב-Android TVReceiver כדי לשמור על התנהגות עקבית בפלטפורמות השונות.

ב-Android, מציינים הפסקות למודעות בפקודת טעינה באמצעות AdBreakClipInfo ו-AdBreakInfo:

קוטלין
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" />
קוטלין
// 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:

קוטלין
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:

קוטלין
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 כדי לשלוח למקבל הודעות באמצעות מחרוזות בערוץ:

קוטלין
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 להפעלה אוטומטית ולהוספת תורים.

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

רכיבים שונים של ה-framework (למשל, תיבת הדו-שיח של הפעלת 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, כך שלא צריך לעשות כלום.
  • אם הגדרתם את לחצן ההעברה באמצעות CastButtonFactory.setUpMediaRouteButton(Context, Menu, int) או CastButtonFactory.setUpMediaRouteButton(Context, MediaRouteButton), תיבות הדו-שיח נוצרות בפועל באמצעות MediaRouteChooserDialog ו-MediaRouteControllerDialog, כך שלא צריך לעשות שום דבר.
  • במקרים אחרים, צריך ליצור תיבות דו-שיח מותאמות אישית של הפעלת Cast, ולכן צריך לפעול לפי ההוראות שלמעלה כדי לעדכן את מצב בחירת המסלול ב-MediaRouter.

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

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

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

השלבים הבאים

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