הוספה של תכונות מתקדמות לאפליקציית 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);
        }
    }
}

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

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

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

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

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

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

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

SessionManager הוא המקום המרכזי לניהול מחזור החיים של הסשן. ‫SessionManager listens to Android MediaRouter route selection state changes to start, resume and end sessions. כשבוחרים מסלול, 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 או אינטרנט), או ליצור אפליקציית מקלט לאינטרנט.