Trình chuyển đổi đầu ra là một tính năng của SDK Truyền, cho phép chuyển đổi liền mạch giữa chế độ phát nội dung cục bộ và từ xa kể từ Android 13. Mục tiêu của việc này là giúp các ứng dụng gửi dễ dàng và nhanh chóng kiểm soát vị trí phát nội dung.
Trình chuyển đổi đầu ra sử dụng thư viện MediaRouter
để chuyển đổi chế độ phát nội dung giữa loa điện thoại, thiết bị Bluetooth đã ghép nối và các thiết bị từ xa có hỗ trợ tính năng Truyền. Các trường hợp sử dụng có thể được chia thành các trường hợp sau:
Tải xuống và sử dụng ứng dụng mẫu CastVideos-android để tham khảo cách triển khai Trình chuyển đổi đầu ra trong ứng dụng.
Bạn nên bật Trình chuyển đổi đầu ra để hỗ trợ chế độ cục bộ sang từ xa, từ xa sang cục bộ và từ xa sang từ xa bằng cách làm theo các bước trong hướng dẫn này. Bạn không cần thực hiện thêm bước nào để hỗ trợ việc chuyển đổi giữa loa thiết bị cục bộ và thiết bị Bluetooth đã ghép nối.
Giao diện người dùng của Trình chuyển đổi đầu ra
Trình chuyển đổi đầu ra hiển thị các thiết bị cục bộ và từ xa có sẵn cũng như trạng thái thiết bị hiện tại, bao gồm cả việc thiết bị có được chọn hay không, đang kết nối hay không, mức âm lượng hiện tại. Nếu có các thiết bị khác ngoài thiết bị hiện tại, thì khi nhấp vào thiết bị khác, bạn có thể chuyển chế độ phát nội dung nghe nhìn sang thiết bị đã chọn.
Vấn đề đã biết
- Các Phiên phát nội dung nghe nhìn được tạo để phát trên thiết bị sẽ bị đóng và tạo lại khi chuyển sang thông báo của SDK Truyền.
Điểm truy cập
Thông báo về nội dung nghe nhìn
Nếu một ứng dụng đăng thông báo nội dung nghe nhìn bằng MediaSession
để phát nội dung cục bộ (phát cục bộ), thì góc trên cùng bên phải của thông báo nội dung nghe nhìn sẽ hiển thị một khối thông báo có tên thiết bị (chẳng hạn như loa điện thoại) mà nội dung đang phát. Khi nhấn vào khối thông báo, giao diện người dùng hệ thống của hộp thoại Trình chuyển đổi đầu ra sẽ mở ra.
Cài đặt âm lượng
Bạn cũng có thể kích hoạt giao diện người dùng hệ thống của hộp thoại Trình chuyển đổi đầu ra bằng cách nhấp vào các nút âm lượng thực trên thiết bị, nhấn vào biểu tượng cài đặt ở dưới cùng rồi nhấn vào văn bản "Phát <Tên ứng dụng> trên <Thiết bị truyền>".
Tóm tắt các bước
- Đảm bảo đáp ứng các điều kiện tiên quyết
- Bật Trình chuyển đổi đầu ra trong AndroidManifest.xml
- Cập nhật SessionManagerListener để truyền trong nền
- Thêm tính năng hỗ trợ cho Remote-to-Remote
- Đặt cờ setRemoteToLocalEnabled
- Tiếp tục phát trên thiết bị
Điều kiện tiên quyết
- Di chuyển ứng dụng Android hiện có sang AndroidX.
- Cập nhật
build.gradle
của ứng dụng để sử dụng phiên bản tối thiểu bắt buộc của SDK Trình gửi Android cho Trình chuyển đổi đầu ra:dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- Ứng dụng hỗ trợ thông báo nội dung nghe nhìn.
- Thiết bị chạy Android 13.
Thiết lập Thông báo về nội dung nghe nhìn
Để sử dụng Trình chuyển đổi đầu ra, ứng dụng âm thanh và video phải tạo thông báo đa phương tiện để hiển thị trạng thái phát và các chế độ điều khiển cho nội dung đa phương tiện để phát trên thiết bị. Để làm việc này, bạn cần tạo một MediaSession
, thiết lập MediaStyle
bằng mã thông báo của MediaSession
và thiết lập các chế độ điều khiển nội dung nghe nhìn trên thông báo.
Nếu bạn hiện không sử dụng MediaStyle
và MediaSession
, đoạn mã dưới đây sẽ cho biết cách thiết lập các thành phần này và có hướng dẫn để thiết lập lệnh gọi lại phiên nội dung đa phương tiện cho ứng dụng âm thanh và video:
// 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(); }
Ngoài ra, để điền thông tin về nội dung nghe nhìn vào thông báo, bạn cần thêm siêu dữ liệu và trạng thái phát của nội dung nghe nhìn vào MediaSession
.
Để thêm siêu dữ liệu vào MediaSession
, hãy sử dụng setMetaData()
và cung cấp tất cả hằng số MediaMetadata
có liên quan cho nội dung nghe nhìn trong 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() ); }
Để thêm trạng thái phát vào MediaSession
, hãy sử dụng setPlaybackState()
và cung cấp tất cả hằng số PlaybackStateCompat
liên quan cho nội dung nghe nhìn trong 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() ); }
Hành vi của thông báo trong ứng dụng video
Các ứng dụng video hoặc ứng dụng âm thanh không hỗ trợ phát nội dung cục bộ ở chế độ nền phải có hành vi cụ thể đối với thông báo đa phương tiện để tránh các vấn đề khi gửi lệnh đa phương tiện trong trường hợp không hỗ trợ phát:
- Đăng thông báo về nội dung nghe nhìn khi phát nội dung nghe nhìn trên thiết bị và ứng dụng đang chạy ở nền trước.
- Tạm dừng phát nội dung cục bộ và đóng thông báo khi ứng dụng chạy ở chế độ nền.
- Khi ứng dụng chuyển về nền trước, quá trình phát nội dung cục bộ sẽ tiếp tục và thông báo sẽ được đăng lại.
Bật Trình chuyển đổi đầu ra trong AndroidManifest.xml
Để bật Trình chuyển đổi đầu ra, bạn cần thêm MediaTransferReceiver
vào AndroidManifest.xml
của ứng dụng. Nếu không, tính năng này sẽ không được bật và cờ tính năng từ xa đến cục bộ cũng sẽ không hợp lệ.
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
MediaTransferReceiver
là một broadcast receiver cho phép chuyển nội dung nghe nhìn giữa các thiết bị có giao diện người dùng hệ thống. Hãy xem tài liệu tham khảo MediaTransferReceiver để biết thêm thông tin.
Từ cục bộ đến từ xa
Khi người dùng chuyển đổi chế độ phát từ cục bộ sang từ xa, SDK truyền sẽ tự động bắt đầu phiên truyền. Tuy nhiên, các ứng dụng cần xử lý việc chuyển đổi từ cục bộ sang từ xa, ví dụ: dừng phát nội dung cục bộ và tải nội dung đa phương tiện trên thiết bị Truyền. Các ứng dụng nên nghe SessionManagerListener
của tính năng Truyền, sử dụng lệnh gọi lại onSessionStarted()
và onSessionEnded()
, đồng thời xử lý thao tác khi nhận được lệnh gọi lại SessionManager
của tính năng Truyền. Ứng dụng phải đảm bảo rằng các lệnh gọi lại này vẫn hoạt động khi hộp thoại Trình chuyển đổi đầu ra được mở và ứng dụng không ở nền trước.
Cập nhật SessionManagerListener để truyền trong nền
Trải nghiệm truyền cũ đã hỗ trợ tính năng truyền từ cục bộ đến từ xa khi ứng dụng ở chế độ nền trước. Trải nghiệm truyền thông thường bắt đầu khi người dùng nhấp vào biểu tượng Truyền trong ứng dụng và chọn một thiết bị để truyền trực tuyến nội dung nghe nhìn. Trong trường hợp này, ứng dụng cần đăng ký với SessionManagerListener
trong onCreate()
hoặc onStart()
và huỷ đăng ký trình nghe trong onStop()
hoặc onDestroy()
của hoạt động của ứng dụng.
Với trải nghiệm truyền mới bằng Trình chuyển đổi đầu ra, các ứng dụng có thể bắt đầu truyền khi đang chạy ở chế độ nền. Điều này đặc biệt hữu ích đối với các ứng dụng âm thanh đăng thông báo khi phát ở chế độ nền. Ứng dụng có thể đăng ký trình nghe SessionManager
trong onCreate()
của dịch vụ và huỷ đăng ký trong onDestroy()
của dịch vụ. Ứng dụng phải luôn nhận được lệnh gọi lại từ cục bộ đến từ xa (chẳng hạn như onSessionStarted
) khi ứng dụng đang chạy ở chế độ nền.
Nếu ứng dụng sử dụng MediaBrowserService
, bạn nên đăng ký SessionManagerListener
tại đó.
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); } } }
Với bản cập nhật này, tính năng truyền từ thiết bị cục bộ đến thiết bị từ xa hoạt động giống như tính năng truyền thông thường khi ứng dụng chạy ở chế độ nền và bạn không cần phải làm gì thêm để chuyển từ thiết bị Bluetooth sang thiết bị Cast.
Từ xa đến cục bộ
Trình chuyển đổi đầu ra cho phép chuyển từ chế độ phát từ xa sang loa điện thoại hoặc thiết bị Bluetooth cục bộ. Bạn có thể bật tính năng này bằng cách đặt cờ setRemoteToLocalEnabled
thành true
trên CastOptions
.
Trong trường hợp thiết bị gửi hiện tại tham gia một phiên hiện có với nhiều thiết bị gửi và ứng dụng cần kiểm tra xem nội dung nghe nhìn hiện tại có được phép chuyển cục bộ hay không, thì ứng dụng nên sử dụng lệnh gọi lại onTransferred
của SessionTransferCallback
để kiểm tra SessionState
.
Đặt cờ setRemoteToLocalEnabled
CastOptions.Builder
cung cấp setRemoteToLocalEnabled
để hiển thị hoặc ẩn loa điện thoại và các thiết bị Bluetooth cục bộ dưới dạng mục tiêu chuyển đến trong hộp thoại Trình chuyển đổi đầu ra khi có phiên Truyền đang hoạt động.
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() } }
Tiếp tục phát trên thiết bị
Các ứng dụng hỗ trợ từ xa đến cục bộ nên đăng ký SessionTransferCallback
để nhận thông báo khi sự kiện xảy ra. Nhờ đó, các ứng dụng có thể kiểm tra xem có cho phép truyền và tiếp tục phát nội dung nghe nhìn cục bộ hay không.
CastContext#addSessionTransferCallback(SessionTransferCallback)
cho phép ứng dụng đăng ký SessionTransferCallback
và nghe lệnh gọi lại onTransferred
và onTransferFailed
khi một trình gửi được chuyển sang chế độ phát cục bộ.
Sau khi ứng dụng huỷ đăng ký SessionTransferCallback
, ứng dụng sẽ không còn nhận được SessionTransferCallback
nữa.
SessionTransferCallback
là phần mở rộng của các lệnh gọi lại SessionManagerListener
hiện có và được kích hoạt sau khi onSessionEnded
được kích hoạt. Thứ tự của lệnh gọi lại từ xa đến cục bộ là:
onTransferring
onSessionEnding
onSessionEnded
onTransferred
Vì khối thông báo đa phương tiện có thể mở Trình chuyển đổi đầu ra khi ứng dụng đang chạy ở chế độ nền và truyền, nên các ứng dụng cần xử lý việc chuyển sang cục bộ theo cách khác nhau tuỳ thuộc vào việc ứng dụng có hỗ trợ phát ở chế độ nền hay không. Trong trường hợp chuyển không thành công, onTransferFailed
sẽ kích hoạt bất cứ khi nào lỗi xảy ra.
Các ứng dụng hỗ trợ phát ở chế độ nền
Đối với các ứng dụng hỗ trợ phát ở chế độ nền (thường là ứng dụng âm thanh), bạn nên sử dụng Service
(ví dụ: MediaBrowserService
). Các dịch vụ phải theo dõi lệnh gọi lại onTransferred
và tiếp tục phát cục bộ cả khi ứng dụng ở chế độ nền trước hoặc nền.
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. } } }
Ứng dụng không hỗ trợ chế độ phát trong nền
Đối với các ứng dụng không hỗ trợ chế độ phát ở chế độ nền (thường là các ứng dụng video), bạn nên nghe lệnh gọi lại onTransferred
và tiếp tục phát cục bộ nếu ứng dụng đang ở nền trước.
Nếu đang chạy ở chế độ nền, ứng dụng sẽ tạm dừng phát và lưu trữ thông tin cần thiết từ SessionState
(ví dụ: siêu dữ liệu nội dung nghe nhìn và vị trí phát). Khi ứng dụng chuyển từ chế độ nền sang chế độ nền trước, quá trình phát nội dung cục bộ sẽ tiếp tục bằng thông tin đã lưu trữ.
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. } } }
Từ xa đến từ xa
Trình chuyển đổi đầu ra hỗ trợ khả năng mở rộng sang nhiều thiết bị loa hỗ trợ tính năng Truyền cho các ứng dụng âm thanh bằng cách sử dụng tính năng Mở rộng luồng.
Ứng dụng âm thanh là những ứng dụng hỗ trợ Google Cast cho Âm thanh trong phần cài đặt Ứng dụng nhận trong Bảng điều khiển dành cho nhà phát triển SDK của Google Cast
Mở rộng phiên phát trực tuyến bằng loa
Các ứng dụng âm thanh sử dụng Trình chuyển đổi đầu ra có thể mở rộng âm thanh sang nhiều thiết bị loa hỗ trợ tính năng Truyền trong một phiên Truyền bằng cách sử dụng tính năng Mở rộng luồng.
Nền tảng Cast hỗ trợ tính năng này và không yêu cầu thay đổi nào khác nếu ứng dụng đang sử dụng giao diện người dùng mặc định. Nếu sử dụng giao diện người dùng tuỳ chỉnh, ứng dụng sẽ cập nhật giao diện người dùng để phản ánh việc ứng dụng đang truyền đến một nhóm.
Để lấy tên nhóm mở rộng mới trong quá trình mở rộng luồng, hãy đăng ký một Cast.Listener
bằng CastSession#addCastListener
.
Sau đó, hãy gọi CastSession#getCastDevice()
trong lệnh gọi lại 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); } }
Kiểm thử từ xa đến từ xa
Cách kiểm thử tính năng này:
- Truyền nội dung đến một thiết bị hỗ trợ tính năng Truyền bằng cách truyền thông thường hoặc bằng tính năng chuyển từ cục bộ sang từ xa.
- Mở Trình chuyển đổi đầu ra bằng một trong các điểm truy cập.
- Nhấn vào một thiết bị khác có hỗ trợ tính năng Truyền, các ứng dụng âm thanh sẽ mở rộng nội dung sang thiết bị bổ sung, tạo một nhóm động.
- Nhấn lại vào thiết bị hỗ trợ tính năng Truyền, thiết bị đó sẽ bị xoá khỏi nhóm động.