출력 전환기는 원활한 전송을 지원하는 Cast SDK의 기능입니다.
콘텐츠의 로컬 재생과 원격 재생 간의 차이점이 Android 13부터 지원됩니다. 목표
발신기 앱이 콘텐츠 재생 위치를 쉽고 빠르게 제어할 수 있게 하는 것입니다.
출력 전환기는
MediaRouter
라이브러리를 사용하여
휴대전화 스피커, 페어링된 블루투스 기기,
및 원격 Cast 지원 기기. 사용 사례는 다음과 같이 분류할 수 있습니다.
시나리오:
CastVideos-android 샘플 앱을 다운로드하여 사용합니다. 앱에서 출력 전환기를 구현하는 방법을 참고하세요.
로컬-원격, 원격-로컬을 지원하도록 출력 전환기를 사용 설정해야 합니다. 원격 및 원격 간 전환만 가능합니다. 없음 로컬 기기 간 전송을 지원하기 위해 필요한 추가 단계 스피커, 페어링된 블루투스 기기
출력 전환기 UI
출력 전환기에 사용 가능한 로컬 기기와 원격 기기가 표시됩니다. 현재 기기 상태(기기 선택 여부, 연결 중입니다. 현재 볼륨 레벨입니다. 이 외에 다른 기기가 있는 경우 다른 기기를 클릭하면 미디어를 전송할 수 있습니다. 선택한 기기로 재생됩니다.
알려진 문제
- 로컬 재생을 위해 만든 미디어 세션이 닫혔다가 다시 생성됩니다. '전송' SDK 알림으로 전환할 때 사용할 수 있습니다
진입점
미디어 알림
앱이
MediaSession
:
로컬 재생 (로컬에서 재생), 미디어 알림의 오른쪽 상단
알림을 받을 수 있는 기기 이름 (예: 휴대전화 스피커)이 포함된 알림 칩
현재 콘텐츠가 재생되고 있습니다. 알림 칩을 탭하면 열립니다
출력 전환기 대화상자 시스템 UI입니다.
볼륨 설정
출력 전환기 대화상자 시스템 UI는 기기의 실제 볼륨 버튼, 하단의 설정 아이콘 탭, '<App Name> 재생'을 탭하여 <Cast 기기>에서 재생 있습니다.
단계 요약
- 기본 요건 충족
- AndroidManifest.xml에서 출력 전환기 사용 설정
- 백그라운드 전송을 위한 SessionManagerListener 업데이트
- Remote-to-Remote 지원 추가
- setRemoteToLocalEnabled 플래그 설정
- 로컬에서 계속 재생
기본 요건
- 기존 Android 앱을 AndroidX로 이전합니다.
- 앱의
build.gradle
을 업데이트하여 필요한 최소 버전 출력 전환기용 Android 발신기 SDK:dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- 앱이 미디어 알림을 지원합니다.
- Android 13을 실행하는 기기
미디어 알림 설정
출력 전환기를 사용하려면
audio 및
동영상 앱
미디어 알림을 생성하여 재생 상태와
제어할 수 없습니다. 이를 위해서는
MediaSession
님,
설정
MediaStyle
MediaSession
의 토큰으로 설정하고
있습니다.
현재 MediaStyle
및 MediaSession
를 사용하지 않는 경우 스니펫은
아래에는 설정 방법이 나와 있으며 미디어 설정에 대한 가이드가 제공됩니다.
세션 콜백
audio 및
동영상
앱:
// 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
에 추가해야 합니다. 그렇지 않은 경우 기능은
사용할 수 없으며 원격-로컬 기능 플래그도 유효하지 않습니다.
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
이
MediaTransferReceiver
드림
시스템이 있는 장치 간 미디어 전송을 가능하게 하는 broadcast receiver입니다.
있습니다. 자세한 내용은 MediaTransferReceiver를 참고하세요.
참조
를 참조하세요.
로컬-원격
사용자가 로컬에서 원격으로 재생을 전환하면 Cast SDK가 시작됨
자동으로 전송합니다. 그러나 앱은
원격에서 원격으로(예: 로컬 재생 중지)
Cast 기기에서 미디어를 로드할 수 있습니다. 앱에서 Cast를 청취해야 합니다.
SessionManagerListener
님,
사용
onSessionStarted()
및
onSessionEnded()
요청을 수신하면
SessionManager
있습니다. 앱은 다음과 같은 경우 이러한 콜백이 계속 활성화되도록 해야 합니다.
출력 전환기 대화상자가 열리고 앱이 포그라운드에 있지 않을 때.
백그라운드 전송을 위해 SessionManagerListener 업데이트
기존 전송 환경은 앱이 호환되는 경우 이미 로컬-원격을 지원합니다.
포그라운드로 이동합니다. 일반적인 전송 환경은 사용자가 전송 아이콘을 클릭하면 시작됩니다.
클릭하고 미디어를 스트리밍할 기기를 선택합니다. 이 경우 앱은
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); } } }
이 업데이트를 통해 로컬-리모컨은 앱이 백그라운드에 있고 블루투스 기기에서 Cast 기기로 이동합니다.
원격 로컬 간
출력 전환기를 사용하면 원격 재생에서 출력 장치로
휴대전화 스피커 또는 로컬 블루투스 기기에 연결할 수 있습니다. 이 옵션은
setRemoteToLocalEnabled
드림
CastOptions
에서 true
로 플래그를 지정합니다.
현재 발신자 기기가 다음을 사용하여 기존 세션에 참여하는 케이스
현재 미디어가 현재 미디어에서 여러 발신자를
로컬에서 전송하려면 앱은 onTransferred
를 사용해야 합니다.
SessionTransferCallback
의 콜백
SessionState
를 확인하세요.
setRemoteToLocalEnabled 플래그 설정
CastOptions.Builder
setRemoteToLocalEnabled
를 제공하여 휴대전화 스피커 및 로컬 블루투스 기기를 전송 대상 기기로 표시하거나 숨깁니다.
출력 전환기 대화상자에 표시됩니다.
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
가 트리거된 후에 트리거됩니다. 순서
원격 로컬 간 콜백의 특징은 다음과 같습니다.
onTransferring
onSessionEnding
onSessionEnded
onTransferred
출력 전환기는 미디어 알림 칩으로 열릴 수 있으므로
백그라운드 및 전송 중인 경우 앱에서 로컬로 전송을 처리해야 함
백그라운드 재생 지원 여부에 따라
달라질 수 있습니다 케이스
전송 실패 중, 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 for Audio를 지원하는 앱입니다. Google Cast SDK 개발자 콘솔
스피커를 통한 스트림 확장
출력 전환을 사용하는 오디오 앱에는 오디오를 확장할 수 있는 기능이 있습니다. 스트림을 사용하여 Cast 세션 중에 여러 Cast 지원 스피커 기기로 전송 확장.
이 기능은 Cast 플랫폼에서 지원되며 더 이상 필요하지 않습니다. 앱이 기본 UI를 사용 중이면 변경됩니다. 맞춤 UI를 사용하는 경우 앱이 앱이 그룹으로 전송되고 있음을 반영하도록 UI를 업데이트해야 합니다.
스트림 확장 중에 새로운 확장 그룹 이름을 가져오려면
등록
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); } }
원격 원격 테스트
기능을 테스트하려면 다음 안내를 따르세요.
- 기존 전송 기능을 사용하거나 local-to-remote입니다.
- 진입점 중 하나를 사용하여 출력 전환기를 엽니다.
- 다른 Cast 지원 기기를 탭하면 오디오 앱이 콘텐츠를 추가 기기, 동적 그룹 생성
- Cast 지원 기기를 다시 탭하면 동적 연결에서 삭제됩니다. 그룹