Переключатель вывода – это функция Cast SDK, которая позволяет легко переключаться между локальным и удаленным воспроизведением контента на устройствах с Android 13 и более поздними версиями. Цель состоит в том, чтобы помочь приложениям-отправителям легко и быстро контролировать, где воспроизводится контент.
Переключатель вывода использует библиотеку MediaRouter, чтобы переключать воспроизведение контента между динамиком телефона, подключенными устройствами Bluetooth и удаленными устройствами с поддержкой Cast. Примеры использования можно разделить на следующие сценарии:
Скачайте и используйте образец приложения CastVideos-android, чтобы узнать, как реализовать переключатель вывода в своем приложении.
Чтобы поддерживать переключение между локальным и удаленным, удаленным и локальным, а также удаленным и удаленным устройствами, необходимо включить переключатель вывода, следуя инструкциям в этом руководстве. Для переключения между динамиками устройства и подключенными устройствами Bluetooth не требуется никаких дополнительных действий.
Интерфейс переключателя вывода
Переключатель вывода показывает доступные локальные и удаленные устройства, а также их текущее состояние, например выбрано ли устройство, подключается ли оно и какой уровень громкости установлен. Если кроме текущего устройства есть другие, вы можете переключить воспроизведение на одно из них.

Известные проблемы
- Сеансы, созданные для локального воспроизведения, будут закрыты и созданы заново при переключении на уведомление Cast SDK.
Точки входа
Уведомление о медиаконтенте
Если приложение отправляет уведомление о мультимедиа с тегом MediaSession для локального воспроизведения, в правом верхнем углу уведомления показывается чип с названием устройства, на котором в данный момент воспроизводится контент (например, "Динамик телефона"). При нажатии на чип уведомления открывается системный интерфейс переключателя вывода.

Настройки громкости
Диалоговое окно переключателя вывода также можно открыть, нажав физические кнопки регулировки громкости на устройстве, а затем – значок настроек внизу и текст "Воспроизвести <название приложения> на <название устройства для трансляции>".

Список шагов
- Убедитесь, что выполнены предварительные требования
- Как включить переключатель вывода в файле AndroidManifest.xml
- Как обновить SessionManagerListener для трансляции в фоновом режиме
- Как добавить поддержку Remote-to-Remote
- Как задать флаг setRemoteToLocalEnabled
- Как продолжить воспроизведение на устройстве
Требования
- Перенесите существующее приложение Android на AndroidX.
- Обновите файл
build.gradleприложения, чтобы использовать минимальную требуемую версию Android Sender SDK для переключателя вывода:dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- Приложение поддерживает уведомления о мультимедиа.
- Устройство с Android 13.
Как настроить уведомления о медиаконтенте
Чтобы использовать переключатель вывода, приложения Аудио и Видео должны создать уведомление о медиаконтенте, в котором будет показан статус воспроизведения и элементы управления для локального воспроизведения. Для этого нужно создать MediaSession, задать MediaStyle с токеном MediaSession и настроить элементы управления мультимедиа в уведомлении.
Если вы не используете MediaStyle и MediaSession, то можете настроить их с помощью фрагмента кода ниже. Также доступны руководства по настройке обратных вызовов медиасеанса для аудио- и видеоприложений.
// 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)
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().
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() )
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().
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() )
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 приложения. Если это не так, функция не будет включена, а флаг функции remote-to-local будет недействительным.
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
MediaTransferReceiver – это широковещательный приемник, который позволяет передавать медиаконтент между устройствами с помощью интерфейса системы. Подробную информацию можно найти в справочнике по MediaTransferReceiver.
Локальный – удаленный
Когда пользователь переключает воспроизведение с локального на удаленное, Cast SDK автоматически запускает сеанс Cast. Однако приложения должны уметь переключаться с локального воспроизведения на удаленное, например останавливать локальное воспроизведение и загружать медиаконтент на устройство Cast. Приложения должны прослушивать Cast SessionManagerListener, используя обратные вызовы onSessionStarted() и onSessionEnded(), и обрабатывать действие при получении обратных вызовов Cast SessionManager. Приложения должны обеспечивать работу этих обратных вызовов, когда открыто диалоговое окно переключателя вывода, а приложение не находится на переднем плане.
Обновление SessionManagerListener для трансляции в фоновом режиме
В устаревшей версии Cast уже поддерживается переход с локального устройства на удаленное, когда приложение работает в фоновом режиме. Обычно пользователи нажимают на значок Cast в приложении и выбирают устройство, на которое будет транслироваться контент. В этом случае приложение должно зарегистрироваться в SessionManagerListener, в onCreate() или onStart() и отменить регистрацию прослушивателя в onStop() или onDestroy() действия приложения.
Благодаря новому интерфейсу трансляции с помощью переключателя устройств приложения могут начинать трансляцию, когда они работают в фоновом режиме. Это особенно полезно для аудиоприложений, которые отправляют уведомления при воспроизведении в фоновом режиме. Приложения могут регистрировать слушателей SessionManager в onCreate() сервиса и отменять регистрацию в onDestroy() сервиса. Приложения всегда должны получать обратные вызовы из локальной сети в удаленную (например, onSessionStarted), когда они работают в фоновом режиме.
Если в приложении используется MediaBrowserService, рекомендуется зарегистрировать SessionManagerListener в нем.
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) } } }
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 из SessionTransferCallback, чтобы проверить SessionState.
Установите флажок setRemoteToLocalEnabled
CastOptions.Builder
предоставляет setRemoteToLocalEnabled, чтобы показывать или скрывать динамик телефона и локальные устройства Bluetooth в качестве целевых устройств для передачи в диалоговом окне переключателя вывода, когда активен сеанс трансляции.
class CastOptionsProvider : OptionsProvider { fun getCastOptions(context: Context?): CastOptions { ... return Builder() ... .setRemoteToLocalEnabled(true) .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { ... return new CastOptions.Builder() ... .setRemoteToLocalEnabled(true) .build() } }
Как продолжить воспроизведение на устройстве
Приложения, поддерживающие передачу контента с удаленного устройства на локальное, должны зарегистрировать SessionTransferCallback, чтобы получать уведомления о событии и проверять, можно ли передавать медиаконтент и продолжать воспроизведение на локальном устройстве.
CastContext#addSessionTransferCallback(SessionTransferCallback) позволяет приложению зарегистрировать SessionTransferCallback и прослушивать обратные вызовы onTransferred и onTransferFailed, когда отправитель переходит на локальное воспроизведение.
После того как приложение отменит регистрацию SessionTransferCallback, оно больше не будет получать SessionTransferCallback.
SessionTransferCallback – это расширение существующих обратных вызовов SessionManagerListener и активируется после onSessionEnded. Порядок обратных вызовов из удаленного местоположения в местное:
onTransferringonSessionEndingonSessionEndedonTransferred
Поскольку переключатель вывода можно открыть с помощью чипа уведомления о мультимедиа, когда приложение работает в фоновом режиме и транслирует контент, приложения должны по-разному обрабатывать переход на локальное воспроизведение в зависимости от того, поддерживают ли они фоновое воспроизведение. Если перенос не удастся, тег onTransferFailed будет активирован в момент возникновения ошибки.
Приложения, поддерживающие фоновое воспроизведение
Если приложение поддерживает воспроизведение в фоновом режиме (обычно это аудиоприложения), рекомендуется использовать Service (например, MediaBrowserService). Службы должны прослушивать обратный вызов onTransferred и возобновлять воспроизведение локально, когда приложение находится в активном или фоновом режиме.
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. } } }
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 и возобновить воспроизведение локально, если приложение находится на переднем плане.
Если приложение работает в фоновом режиме, оно должно приостановить воспроизведение и сохранить необходимую информацию из SessionState (например, метаданные медиаконтента и позицию воспроизведения). Когда приложение переходит из фонового режима в активный, воспроизведение должно продолжаться с использованием сохраненной информации.
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. } } }
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 для аудио в настройках приложения-приемника в Google Cast SDK Developer Console.

Расширение трансляции с помощью колонок
Аудиоприложения, в которых используется переключатель вывода, могут расширять трансляцию аудио на несколько колонок с поддержкой Cast.
Эта функция поддерживается платформой Cast и не требует дополнительных изменений, если в приложении используется интерфейс по умолчанию. Если используется пользовательский интерфейс, приложение должно обновить его, чтобы отразить, что приложение транслирует в группу.

Чтобы получить новое название расширенной группы во время расширения трансляции, зарегистрируйте Cast.Listener с помощью CastSession#addCastListener.
Затем вызовите CastSession#getCastDevice() во время обратного вызова onDeviceNameChanged.
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) } }
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); } }
Тестирование удаленного подключения
Чтобы проверить функцию:
- Транслируйте контент на устройство с поддержкой Cast, используя обычную трансляцию или локально-удаленную.
- Откройте переключатель вывода, используя одну из точек входа.
- Нажмите на другое устройство с поддержкой Cast, и аудиоприложения расширят контент на дополнительное устройство, создав динамическую группу.
- Нажмите на устройство с поддержкой Cast ещё раз, и оно будет удалено из динамической группы.