מחליף פלט

מתג המעבר למכשיר אחר הוא תכונה ב-Cast SDK שמאפשרת העברה חלקה בין הפעלה מקומית לבין הפעלה מרחוק של תוכן, החל מ-Android 13. המטרה היא לעזור לשולחים לשלוט בקלות ובמהירות במיקום שבו התוכן מופעל. מתג המעבר למכשיר אחר משתמש MediaRouter להעביר את הפעלת התוכן בין הרמקול של הטלפון, מכשירי Bluetooth מותאמים, ומכשירים מרחוק שתומכים ב-Cast. אפשר לפצל את התרחישים לדוגמה הבאים תרחישים:

להוריד את האפליקציה לדוגמה Cast videos-android ולהשתמש בה כדי ללמוד איך להטמיע את Output Switcher באפליקציה שלך.

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

ממשק המשתמש של מתג המעבר למכשיר אחר

מתג המעבר למכשיר אחר מציג את המכשירים המקומיים והמכשירים המרוחקים הזמינים וגם את מצבי המכשיר הנוכחיים, כולל אם המכשיר נבחר, מתחבר, עוצמת הקול הנוכחית. אם יש מכשירים אחרים בנוסף למכשיר הנוכחי, לחיצה על מכשיר אחר תאפשר לך להעביר את המדיה ההפעלה במכשיר שנבחר.

בעיות ידועות

  • פעילויות מדיה שנוצרו בהפעלה מקומית יבוטלו וייצרו מחדש כשעוברים אל התראת ה-Cast SDK.

נקודות כניסה

התראה של מדיה

אם אפליקציה מפרסמת התראה על מדיה עם MediaSession עבור הפעלה מקומית (הפעלה מקומית), בפינה השמאלית העליונה של ההתראה במדיה מציגה צ'יפ של התראה עם שם המכשיר (למשל רמקול של טלפון) שבו מופעל התוכן כרגע. הקשה על צ'יפ ההתראה נפתחת ממשק המשתמש של מערכת תיבת הדו-שיח 'מתג פלט'.

הגדרות עוצמת הקול

ניתן להפעיל את ממשק המשתמש של מערכת תיבת הדו-שיח של 'כלי להחלפת פלט' גם על ידי לחיצה על לחצני עוצמת הקול הפיזיים במכשיר, הקשה על סמל ההגדרות בחלק התחתון ולהקיש על 'Play <App Name>' ב<מכשיר להפעלת Cast >" טקסט.

סיכום השלבים

דרישות מוקדמות

  1. העברת אפליקציה קיימת ל-Android אל AndroidX.
  2. עליך לעדכן את build.gradle של האפליקציה כדי להשתמש בגרסה המינימלית הנדרשת של SDK של Android Sender למתג המעבר למכשיר אחר:
    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-cast-framework:21.2.0'
      ...
    }
  3. האפליקציה תומכת בהתראות על מדיה.
  4. מכשיר עם Android 13.

הגדרה של התראות מדיה

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

אם לא נעשה כרגע שימוש ב-MediaStyle וב-MediaSession, קטע הקוד בהמשך תוכלו לראות איך להגדיר אותם, ויש מדריכים זמינים להגדרת המדיה סשנים של קריאה חוזרת (callbacks) עבור אודיו ו סרטון אפליקציות:

Kotlin
// Create a media session. NotificationCompat.MediaStyle
// PlayerService is your own Service or Activity responsible for media playback.
val mediaSession = MediaSessionCompat(this, "PlayerService")

// Create a MediaStyle object and supply your media session token to it.
val mediaStyle = Notification.MediaStyle().setMediaSession(mediaSession.sessionToken)

// Create a Notification which is styled by your MediaStyle object.
// This connects your media session to the media controls.
// Don't forget to include a small icon.
val notification = Notification.Builder(this@PlayerService, CHANNEL_ID)
    .setStyle(mediaStyle)
    .setSmallIcon(R.drawable.ic_app_logo)
    .build()

// Specify any actions which your users can perform, such as pausing and skipping to the next track.
val pauseAction: Notification.Action = Notification.Action.Builder(
        pauseIcon, "Pause", pauseIntent
    ).build()
notification.addAction(pauseAction)
Java
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    // Create a media session. NotificationCompat.MediaStyle
    // PlayerService is your own Service or Activity responsible for media playback.
    MediaSession mediaSession = new MediaSession(this, "PlayerService");

    // Create a MediaStyle object and supply your media session token to it.
    Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(mediaSession.getSessionToken());

    // Specify any actions which your users can perform, such as pausing and skipping to the next track.
    Notification.Action pauseAction = Notification.Action.Builder(pauseIcon, "Pause", pauseIntent).build();

    // Create a Notification which is styled by your MediaStyle object.
    // This connects your media session to the media controls.
    // Don't forget to include a small icon.
    String CHANNEL_ID = "CHANNEL_ID";
    Notification notification = new Notification.Builder(this, CHANNEL_ID)
        .setStyle(mediaStyle)
        .setSmallIcon(R.drawable.ic_app_logo)
        .addAction(pauseAction)
        .build();
}

בנוסף, כדי לאכלס בהתראה את פרטי המדיה, תצטרכו להוסיף את מטא-נתונים ומצב הפעלה אל MediaSession.

כדי להוסיף מטא-נתונים אל MediaSession, צריך להשתמש setMetaData() ולספק את כל קבועים של MediaMetadata עבור המדיה MediaMetadataCompat.Builder().

Kotlin
mediaSession.setMetadata(MediaMetadataCompat.Builder()
    // Title
    .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title)

    // Artist
    // Could also be the channel name or TV series.
    .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist)

    // Album art
    // Could also be a screenshot or hero image for video content
    // The URI scheme needs to be "content", "file", or "android.resource".
    .putString(
        MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri)
    )

    // Duration
    // If duration isn't set, such as for live broadcasts, then the progress
    // indicator won't be shown on the seekbar.
    .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration)

    .build()
)
Java
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    mediaSession.setMetadata(
        new MediaMetadataCompat.Builder()
        // Title
        .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title)

        // Artist
        // Could also be the channel name or TV series.
        .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist)

        // Album art
        // Could also be a screenshot or hero image for video content
        // The URI scheme needs to be "content", "file", or "android.resource".
        .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri)

        // Duration
        // If duration isn't set, such as for live broadcasts, then the progress
        // indicator won't be shown on the seekbar.
        .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration)

        .build()
    );
}

כדי להוסיף את מצב ההפעלה אל MediaSession, צריך להשתמש ב- setPlaybackState() ולספק את כל PlaybackStateCompat קבועים למדיה PlaybackStateCompat.Builder().

Kotlin
mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setState(
            PlaybackStateCompat.STATE_PLAYING,

            // Playback position
            // Used to update the elapsed time and the progress bar.
            mediaPlayer.currentPosition.toLong(),

            // Playback speed
            // Determines the rate at which the elapsed time changes.
            playbackSpeed
        )

        // isSeekable
        // Adding the SEEK_TO action indicates that seeking is supported
        // and makes the seekbar position marker draggable. If this is not
        // supplied seek will be disabled but progress will still be shown.
        .setActions(PlaybackStateCompat.ACTION_SEEK_TO)
        .build()
)
Java
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    mediaSession.setPlaybackState(
        new PlaybackStateCompat.Builder()
            .setState(
                 PlaybackStateCompat.STATE_PLAYING,

                // Playback position
                // Used to update the elapsed time and the progress bar.
                mediaPlayer.currentPosition.toLong(),

                // Playback speed
                // Determines the rate at which the elapsed time changes.
                playbackSpeed
            )

        // isSeekable
        // Adding the SEEK_TO action indicates that seeking is supported
        // and makes the seekbar position marker draggable. If this is not
        // supplied seek will be disabled but progress will still be shown.
        .setActions(PlaybackStateCompat.ACTION_SEEK_TO)
        .build()
    );
}

התנהגות ההתראות של אפליקציית וידאו

אפליקציות וידאו או אפליקציות אודיו שלא תומכות בהפעלה מקומית ברקע צריך להתנהג באופן ספציפי עבור התראות מדיה כדי להימנע מבעיות שליחת פקודות מדיה במצבים שבהם אין תמיכה בהפעלה:

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

יש להפעיל את מתג המעבר למכשיר AndroidManifest.xml

כדי להפעיל את מתג המעבר למכשיר אחר, MediaTransferReceiver צריך להוסיף את האפליקציה ל-AndroidManifest.xml של האפליקציה. אם לא, התכונה לא תופעל, וסימון התכונה 'מרחוק מרחוק' גם לא יהיה חוקי.

<application>
    ...
    <receiver
         android:name="androidx.mediarouter.media.MediaTransferReceiver"
         android:exported="true">
    </receiver>
    ...
</application>

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

מקומי למרוחק

כשהמשתמש יעביר את ההפעלה ממקומית לשלט הרחוק, ה-Cast SDK יתחיל. את הפעלת ה-Cast באופן אוטומטי. אבל האפליקציות צריכות להתמודד עם המעבר מקומי מרחוק, לדוגמה, להפסיק את ההפעלה המקומית וטוענים את המדיה במכשיר ה-Cast. האפליקציות צריכות להאזין להפעלת Cast SessionManagerListener, באמצעות onSessionStarted() וגם onSessionEnded() קריאה חוזרת (callback) וטיפול בפעולה בזמן קבלת ההעברה SessionManager קריאה חוזרת (callback). האפליקציות צריכות להבטיח שהקריאות החוזרות (callback) האלה עדיין יהיו פעילות כאשר תיבת הדו-שיח של Output Switcher פתוחה והאפליקציה לא בחזית.

עדכון של sessionManagerListener עבור העברה ברקע

בגרסה הקודמת של Cast, כבר יש תמיכה במעבר בין חנויות מקומיות לשלט רחוק כשהאפליקציה בחזית. חוויה אופיינית של Cast מתחילה כשמשתמשים לוחצים על סמל ההעברה באפליקציה ולבחור מכשיר לשידור מדיה. במקרה כזה, האפליקציה צריכה להירשם SessionManagerListener, ב-onCreate() או onStart() ומבטלים את הרישום של המאזינים onStop() או onDestroy() בפעילות של האפליקציה.

החוויה החדשה של העברה (cast) באמצעות מתג המעבר למכשיר אחר מאפשרת לאפליקציות להתחיל להפעיל Cast כשהמכשירים ברקע. האפשרות הזאת שימושית במיוחד לאודיו אפליקציות שמפרסמות התראות גם בזמן ההפעלה שלהן ברקע. אפליקציות יכולות לרשום SessionManager מאזינים ב-onCreate() של השירות ומבטלים את הרישום ב-onDestroy() של השירות. האפליקציות תמיד צריכות לקבל את הקריאה החוזרת (callback) מהמיקום לרשת המקומית (כמו בתור onSessionStarted) כשהאפליקציה פועלת ברקע.

אם האפליקציה משתמשת MediaBrowserService, מומלץ לרשום את SessionManagerListener שם.

Kotlin
class MyService : Service() {
    private var castContext: CastContext? = null
    protected fun onCreate() {
        castContext = CastContext.getSharedInstance(this)
        castContext
            .getSessionManager()
            .addSessionManagerListener(sessionManagerListener, CastSession::class.java)
    }

    protected fun onDestroy() {
        if (castContext != null) {
            castContext
                .getSessionManager()
                .removeSessionManagerListener(sessionManagerListener, CastSession::class.java)
        }
    }
}
Java
public class MyService extends Service {
  private CastContext castContext;

  @Override
  protected void onCreate() {
     castContext = CastContext.getSharedInstance(this);
     castContext
        .getSessionManager()
        .addSessionManagerListener(sessionManagerListener, CastSession.class);
  }

  @Override
  protected void onDestroy() {
    if (castContext != null) {
       castContext
          .getSessionManager()
          .removeSessionManagerListener(sessionManagerListener, CastSession.class);
    }
  }
}

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

מרחוק ליעד מקומי

מתג המעבר למכשיר אחר מאפשר לעבור מהפעלה מרחוק אל רמקול של טלפון או מכשיר Bluetooth מקומי. כדי להפעיל את האפשרות הזו, setRemoteToLocalEnabled לסמן ל-true ב-CastOptions.

במקרים שבהם המכשיר השולח הנוכחי מצטרף לסשן קיים עם שולחים מרובים, והאפליקציה צריכה לבדוק אם המדיה הנוכחית מורשית מועברות באופן מקומי, אפליקציות צריכות להשתמש בonTransferred קריאה חוזרת (callback) של SessionTransferCallback כדי לבדוק את SessionState.

הגדרת הסימון setremoteToLocalEnabled

CastOptions.Builder מספק setRemoteToLocalEnabled כדי להציג או להסתיר את הרמקול של הטלפון ואת מכשירי ה-Bluetooth המקומיים כמכשירים להעברה אל יעדים בתיבת הדו-שיח 'מתג מעבר' כשיש סשן Cast פעיל.

Kotlin
class CastOptionsProvider : OptionsProvider {
    fun getCastOptions(context: Context?): CastOptions {
        ...
        return Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
    }
}
Java
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        ...
        return new CastOptions.Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
  }
}

המשך ההפעלה באופן מקומי

צריך לרשום את SessionTransferCallback באפליקציות שתומכות בהמרה מרחוק לאזור מקומי כדי לקבל התראה כשהאירוע מתרחש, כדי שהם יוכלו לבדוק אם צריך להוסיף מדיה מורשה להעביר ולהמשיך את ההפעלה באופן מקומי.

CastContext#addSessionTransferCallback(SessionTransferCallback) מאפשרת לאפליקציה לרשום את ה-SessionTransferCallback שלה ולהאזין לקריאות חוזרות (callback) של onTransferred ו-onTransferFailed כשהשולח הועברו להפעלה מקומית.

אחרי שהאפליקציה מבטלת את הרישום של SessionTransferCallback, האפליקציה לא תקבל יותר SessionTransferCallback s.

SessionTransferCallback הוא הרחבה של SessionManagerListener הקיים קריאה חוזרת (callback) ומופעלת אחרי הפעלת onSessionEnded. הסדר של קריאות חוזרות (callback) של העברה מרחוק לאזור הן:

  1. onTransferring
  2. onSessionEnding
  3. onSessionEnded
  4. onTransferred

מאחר שניתן לפתוח את מתג המעבר למכשיר אחר באמצעות צ'יפ ההתראה של המדיה, האפליקציה פועלת ברקע ומבצעת העברה (cast), האפליקציות צריכות לטפל בהעברה לקובץ המקומי בהתאם לתמיכה בהפעלה ברקע, או לא. במקרה של העברה שנכשלה, onTransferFailed יופעלו בכל פעם שהשגיאה מתרחשת.

אפליקציות שתומכות בהפעלה ברקע

כשמדובר באפליקציות שתומכות בהפעלה ברקע (בדרך כלל באפליקציות אודיו), התשובה תהיה מומלץ להשתמש ב-Service (לדוגמה MediaBrowserService). Services (שירותים) צריך להאזין ל-onTransferred קריאה חוזרת (callback) והמשך ההפעלה באופן מקומי גם כשהאפליקציה בחזית וגם רקע.

Kotlin
class MyService : Service() {
    private var castContext: CastContext? = null
    private var sessionTransferCallback: SessionTransferCallback? = null
    protected fun onCreate() {
        castContext = CastContext.getSharedInstance(this)
        castContext.getSessionManager()
                   .addSessionManagerListener(sessionManagerListener, CastSession::class.java)
        sessionTransferCallback = MySessionTransferCallback()
        castContext.addSessionTransferCallback(sessionTransferCallback)
    }

    protected fun onDestroy() {
        if (castContext != null) {
            castContext.getSessionManager()
                       .removeSessionManagerListener(sessionManagerListener, CastSession::class.java)
            if (sessionTransferCallback != null) {
                castContext.removeSessionTransferCallback(sessionTransferCallback)
            }
        }
    }

    class MySessionTransferCallback : SessionTransferCallback() {
        fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) {
            // Perform necessary steps prior to onTransferred
        }

        fun onTransferred(@SessionTransferCallback.TransferType transferType: Int,
                          sessionState: SessionState?) {
            if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) {
                // Remote stream is transferred to the local device.
                // Retrieve information from the SessionState to continue playback on the local player.
            }
        }

        fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int,
                             @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) {
            // Handle transfer failure.
        }
    }
}
Java
public class MyService extends Service {
    private CastContext castContext;
    private SessionTransferCallback sessionTransferCallback;

    @Override
    protected void onCreate() {
        castContext = CastContext.getSharedInstance(this);
        castContext.getSessionManager()
                   .addSessionManagerListener(sessionManagerListener, CastSession.class);
        sessionTransferCallback = new MySessionTransferCallback();
        castContext.addSessionTransferCallback(sessionTransferCallback);
    }

    @Override
    protected void onDestroy() {
        if (castContext != null) {
            castContext.getSessionManager()
                       .removeSessionManagerListener(sessionManagerListener, CastSession.class);
            if (sessionTransferCallback != null) {
                castContext.removeSessionTransferCallback(sessionTransferCallback);
            }
        }
    }

    public static class MySessionTransferCallback extends SessionTransferCallback {
        public MySessionTransferCallback() {}

        @Override
        public void onTransferring(@SessionTransferCallback.TransferType int transferType) {
            // Perform necessary steps prior to onTransferred
        }

        @Override
        public void onTransferred(@SessionTransferCallback.TransferType int transferType,
                                  SessionState sessionState) {
            if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) {
                // Remote stream is transferred to the local device.
                // Retrieve information from the SessionState to continue playback on the local player.
            }
        }

        @Override
        public void onTransferFailed(@SessionTransferCallback.TransferType int transferType,
                                     @SessionTransferCallback.TransferFailedReason int transferFailedReason) {
            // Handle transfer failure.
        }
    }
}

אפליקציות שלא תומכות בהפעלה ברקע

כשמדובר באפליקציות שלא תומכות בהפעלה ברקע (בדרך כלל אפליקציות וידאו), ניתן מומלץ להאזין ל-onTransferred קריאה חוזרת (callback) והמשך ההפעלה באופן מקומי אם האפליקציה פועלת בחזית.

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

Kotlin
class MyActivity : AppCompatActivity() {
    private var castContext: CastContext? = null
    private var sessionTransferCallback: SessionTransferCallback? = null
    protected fun onCreate() {
        castContext = CastContext.getSharedInstance(this)
        castContext.getSessionManager()
                   .addSessionManagerListener(sessionManagerListener, CastSession::class.java)
        sessionTransferCallback = MySessionTransferCallback()
        castContext.addSessionTransferCallback(sessionTransferCallback)
    }

    protected fun onDestroy() {
        if (castContext != null) {
            castContext.getSessionManager()
                       .removeSessionManagerListener(sessionManagerListener, CastSession::class.java)
            if (sessionTransferCallback != null) {
                castContext.removeSessionTransferCallback(sessionTransferCallback)
            }
        }
    }

    class MySessionTransferCallback : SessionTransferCallback() {
        fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) {
            // Perform necessary steps prior to onTransferred
        }

        fun onTransferred(@SessionTransferCallback.TransferType transferType: Int,
                          sessionState: SessionState?) {
            if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) {
                // Remote stream is transferred to the local device.

                // Retrieve information from the SessionState to continue playback on the local player.
            }
        }

        fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int,
                             @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) {
            // Handle transfer failure.
        }
    }
}
Java
public class MyActivity extends AppCompatActivity {
  private CastContext castContext;
  private SessionTransferCallback sessionTransferCallback;

  @Override
  protected void onCreate() {
     castContext = CastContext.getSharedInstance(this);
     castContext
        .getSessionManager()
        .addSessionManagerListener(sessionManagerListener, CastSession.class);
     sessionTransferCallback = new MySessionTransferCallback();
     castContext.addSessionTransferCallback(sessionTransferCallback);
  }

  @Override
  protected void onDestroy() {
    if (castContext != null) {
       castContext
          .getSessionManager()
          .removeSessionManagerListener(sessionManagerListener, CastSession.class);
      if (sessionTransferCallback != null) {
         castContext.removeSessionTransferCallback(sessionTransferCallback);
      }
    }
  }

  public static class MySessionTransferCallback extends SessionTransferCallback {
    public MySessionTransferCallback() {}

    @Override
    public void onTransferring(@SessionTransferCallback.TransferType int transferType) {
        // Perform necessary steps prior to onTransferred
    }

    @Override
    public void onTransferred(@SessionTransferCallback.TransferType int transferType,
                               SessionState sessionState) {
      if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) {
        // Remote stream is transferred to the local device.

        // Retrieve information from the SessionState to continue playback on the local player.
      }
    }

    @Override
    public void onTransferFailed(@SessionTransferCallback.TransferType int transferType,
                                 @SessionTransferCallback.TransferFailedReason int transferFailedReason) {
      // Handle transfer failure.
    }
  }
}

שליטה מרחוק

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

אפליקציות אודיו הן אפליקציות שתומכות ב-Google Cast לאודיו באפליקצייתReceiver הגדרות ב-Google Cast SDK מסוף

הרחבה של סטרימינג בשידור חי עם רמקולים

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

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

כדי לקבל את השם החדש של הקבוצה המורחבת במהלך הרחבת השידור: לרשום Cast.Listener באמצעות CastSession#addCastListener ואז להתקשר CastSession#getCastDevice() במהלך הקריאה החוזרת של onDeviceNameChanged.

Kotlin
class MyActivity : Activity() {
    private var mCastSession: CastSession? = null
    private lateinit var mCastContext: CastContext
    private lateinit var mSessionManager: SessionManager
    private val mSessionManagerListener: SessionManagerListener<CastSession> =
        SessionManagerListenerImpl()
    private val mCastListener = CastListener()

    private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> {
        override fun onSessionStarting(session: CastSession?) {}

        override fun onSessionStarted(session: CastSession?, sessionId: String) {
            addCastListener(session)
        }

        override fun onSessionStartFailed(session: CastSession?, error: Int) {}

        override fun onSessionSuspended(session: CastSession?, reason Int) {
            removeCastListener()
        }

        override fun onSessionResuming(session: CastSession?, sessionId: String) {}

        override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) {
            addCastListener(session)
        }

        override fun onSessionResumeFailed(session: CastSession?, error: Int) {}

        override fun onSessionEnding(session: CastSession?) {}

        override fun onSessionEnded(session: CastSession?, error: Int) {
            removeCastListener()
        }
    }

    private inner class CastListener : Cast.Listener() {
        override fun onDeviceNameChanged() {
            mCastSession?.let {
                val castDevice = it.castDevice
                val deviceName = castDevice.friendlyName
                // Update UIs with the new cast device name.
            }
        }
    }

    private fun addCastListener(castSession: CastSession) {
        mCastSession = castSession
        mCastSession?.addCastListener(mCastListener)
    }

    private fun removeCastListener() {
        mCastSession?.removeCastListener(mCastListener)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mCastContext = CastContext.getSharedInstance(this)
        mSessionManager = mCastContext.sessionManager
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onDestroy() {
        super.onDestroy()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }
}
Java
public class MyActivity extends Activity {
    private CastContext mCastContext;
    private CastSession mCastSession;
    private SessionManager mSessionManager;
    private SessionManagerListener<CastSession> mSessionManagerListener =
            new SessionManagerListenerImpl();
    private Cast.Listener mCastListener = new CastListener();

    private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> {
        @Override
        public void onSessionStarting(CastSession session) {}
        @Override
        public void onSessionStarted(CastSession session, String sessionId) {
            addCastListener(session);
        }
        @Override
        public void onSessionStartFailed(CastSession session, int error) {}
        @Override
        public void onSessionSuspended(CastSession session, int reason) {
            removeCastListener();
        }
        @Override
        public void onSessionResuming(CastSession session, String sessionId) {}
        @Override
        public void onSessionResumed(CastSession session, boolean wasSuspended) {
            addCastListener(session);
        }
        @Override
        public void onSessionResumeFailed(CastSession session, int error) {}
        @Override
        public void onSessionEnding(CastSession session) {}
        @Override
        public void onSessionEnded(CastSession session, int error) {
            removeCastListener();
        }
    }

    private class CastListener extends Cast.Listener {
         @Override
         public void onDeviceNameChanged() {
             if (mCastSession == null) {
                 return;
             }
             CastDevice castDevice = mCastSession.getCastDevice();
             String deviceName = castDevice.getFriendlyName();
             // Update UIs with the new cast device name.
         }
    }

    private void addCastListener(CastSession castSession) {
        mCastSession = castSession;
        mCastSession.addCastListener(mCastListener);
    }

    private void removeCastListener() {
        if (mCastSession != null) {
            mCastSession.removeCastListener(mCastListener);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCastContext = CastContext.getSharedInstance(this);
        mSessionManager = mCastContext.getSessionManager();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
}

בדיקה של השליטה מרחוק

כדי לבדוק את התכונה:

  1. הפעלת Cast של התוכן למכשיר שתומך ב-Cast באמצעות העברה רגילה או עם מקומי למרוחק.
  2. פותחים את מתג המעבר למכשיר אחר באמצעות אחת מנקודות הכניסה.
  3. מקישים על מכשיר אחר שתומך ב-Cast, אפליקציות אודיו ירחיבו את התוכן במכשיר נוסף, וכך נוצרת קבוצה דינמית.
  4. מקישים שוב על המכשיר שתומך ב-Cast. הוא יוסר מהדינמיקה קבוצה.