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

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

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

Список шагов
- Убедитесь, что выполнены предварительные требования
- Как включить переключатель вывода в файле AndroidManifest.xml
- Обновите SessionManagerListener для фоновой трансляции
- Добавление поддержки удаленного доступа
- Как установить параметр 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 ещё раз, и оно будет удалено из динамической группы.