本開發人員指南說明如何為您的 Android 新增 Google Cast 支援 傳送者應用程式。
行動裝置或筆記型電腦是控製播放的傳送者, Google Cast 裝置是一種接收器,用於在電視上顯示內容。
傳送者架構是指 Cast 類別程式庫二進位檔,以及相關聯的 向傳送端發出的資源配置要求寄件者應用程式或投放應用程式 代表在傳送方上執行的應用程式。網路接收器應用程式 是指在支援 Cast 裝置上執行的 HTML 應用程式。
傳送方架構採用非同步回呼設計來通知傳送者 事件的應用程式中,以及轉換應用程式在不同狀態的切換
應用程式流程
下列步驟說明傳送者的一般高階執行流程 Android 應用程式:
- Cast 架構會自動啟動
MediaRouter
敬上 根據Activity
生命週期探索裝置。 - 當使用者按一下「投放」按鈕時,架構就會顯示「投放」 對話方塊中顯示偵測到的投放裝置清單。
- 使用者選取投放裝置時,架構會嘗試啟動 Cast 裝置上的 Web Receiver 應用程式
- 此架構會在傳送端應用程式中叫用回呼,以確認 Web 接收器應用程式已啟動。
- 這個架構可在傳送者和網頁之間建立通訊管道 接收端應用程式。
- 該架構使用通訊管道來載入及控制媒體 並透過 Web Receiver 播放
- 架構會同步處理傳送者和傳送端的媒體播放狀態 網路接收端:當使用者進行傳送者 UI 動作時,架構會通過 向 Web 接收器發出的媒體控制要求 傳送媒體狀態更新,此架構會更新傳送者 UI 的狀態。
- 當使用者點選「投放」按鈕中斷與投放裝置的連線時, 這個架構會中斷傳送端應用程式與 Web Receiver 的連結。
查看 Google Cast 的所有課程、方法和活動完整清單 Android SDK,請參閱 Google Cast Sender API 參考資料: Android: 以下各節將說明將 Cast 新增至 Android 應用程式的步驟。
設定 Android 資訊清單
應用程式的 AndroidManifest.xml 檔案必須完成下列設定 Cast SDK 的元素:
uses-sdk
設定 Cast SDK 支援的最低和目標 Android API 級別。 目前最低版本為 API 級別 23,目標為 API 級別 API 級別 34。
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
請依據最低 Android SDK 版本設定應用程式主題。舉例來說
您不是實作自己的主題,則應使用
指定的最低 Android SDK 版本時,請設為 Theme.AppCompat
前身為 Lollipop
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
初始化 Cast 結構定義
此架構有一個全域單例模式物件 CastContext
,該座標
所有架構的互動
您的應用程式必須實作
OptionsProvider
敬上
介面,提供初始化
CastContext
單例模式OptionsProvider
提供
CastOptions
,其中包含會影響架構行為的選項。最常出現
重要的是網路接收端應用程式 ID
探索結果,並在投放工作階段
已開始。
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
您必須宣告所實作 OptionsProvider
的完整名稱
做為傳送端應用程式的 AndroidManifest.xml 檔案的中繼資料欄位:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext
會在 CastContext.getSharedInstance()
時延遲初始化
方法。
class MyActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val castContext = CastContext.getSharedInstance(this) } }
public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { CastContext castContext = CastContext.getSharedInstance(this); } }
Cast 使用者體驗小工具
Cast 架構可提供符合 Cast 設計的小工具 檢查清單:
簡介重疊: 架構提供自訂檢視
IntroductoryOverlay
、 要呼叫「投放」按鈕才會向使用者顯示 第一個接收端。寄件者應用程式可以 自訂標題文字和位置 文字。投放按鈕: 無論投放裝置為何,「投放」按鈕都會顯示。 當使用者首次點選「投放」按鈕時,系統會顯示「投放」對話方塊 這裡會列出找到的裝置當使用者按一下「投放」按鈕時 連線時,裝置會顯示目前的媒體中繼資料 (例如 錄影室的名稱、名稱、縮圖),或者允許使用者 即可中斷與投放裝置的連線「投放」按鈕有時也稱為 。
Mini Controller: 使用者投放內容且離開目前畫面時 內容頁面,或展開控制器連往傳送者應用程式的其他畫面, 螢幕底部顯示迷你控制器 查看目前投放的媒體中繼資料,並控製播放作業。
展開控制器: 使用者投放內容時,如果點選媒體通知 會啟動展開的控制器,並顯示 目前正在播放媒體中繼資料,並提供多個按鈕 媒體播放。
通知: 僅限 Android 系統。當使用者投放內容且離開 傳送者應用程式,顯示媒體通知顯示目前正在投放 媒體中繼資料和播放控制項
螢幕鎖定畫面: 僅限 Android 系統。使用者投放內容及瀏覽 (或裝置) 時,螢幕上會顯示媒體螢幕鎖定控制項。 會顯示目前投放媒體中繼資料和播放控制項。
以下指南將說明如何將這些小工具加入
新增「投放」按鈕
Android
MediaRouter
敬上
API 的設計宗旨是在次要裝置上啟用媒體顯示和播放功能。
使用 MediaRouter
API 的 Android 應用程式應隨附「投放」按鈕
使用者介面,讓使用者選取媒體路徑,要在當中播放媒體
或投放裝置等次要裝置。
架構因此加入了一個
MediaRouteButton
敬上
作為
Cast button
非常簡單請先在 XML 中新增選單項目或 MediaRouteButton
檔案定義選單,並使用
CastButtonFactory
並與架構結合
// To add a Cast button, add the following snippet.
// menu.xml
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always" />
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.kt override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.main, menu) CastButtonFactory.setUpMediaRouteButton( applicationContext, menu, R.id.media_route_menu_item ) return true }
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); return true; }
如果 Activity
沿用自
FragmentActivity
,
您可以新增
MediaRouteButton
版面配置。
// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:mediaRouteTypes="user"
android:visibility="gone" />
</LinearLayout>
// MyActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_layout) mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton) mCastContext = CastContext.getSharedInstance(this) }
// MyActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton); mCastContext = CastContext.getSharedInstance(this); }
如要使用主題設定「投放」按鈕的外觀,請參閱: 自訂「投放」按鈕
設定裝置探索功能
探索裝置完全是由
CastContext
。
初始化 CastContext 時,傳送端應用程式會指定網路接收器
應用程式 ID,並可選擇性地透過設定要求命名空間篩選
supportedNamespaces
英吋
CastOptions
。
CastContext
會保留對 MediaRouter
內部的參照,且將以
探索過程:
- 根據經過特別設計的演算法,能平衡裝置探索延遲時間和 因此系統會在以下時間過後自動啟動探索功能: 傳送者應用程式進入前景。
- 「投放」對話方塊已開啟。
- Cast SDK 正嘗試復原投放工作階段。
「投放」對話方塊關閉或 訊息傳送者應用程式進入背景。
class CastOptionsProvider : OptionsProvider { companion object { const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace" } override fun getCastOptions(appContext: Context): CastOptions { val supportedNamespaces: MutableList<String> = ArrayList() supportedNamespaces.add(CUSTOM_NAMESPACE) return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
class CastOptionsProvider implements OptionsProvider { public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"; @Override public CastOptions getCastOptions(Context appContext) { List<String> supportedNamespaces = new ArrayList<>(); supportedNamespaces.add(CUSTOM_NAMESPACE); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
工作階段管理的運作方式
Cast SDK 引進投放工作階段的概念 結合了連線至裝置、啟動 (或加入) 網路的步驟 接收端應用程式,連結至該應用程式,以及初始化媒體控制管道。查看網路接收端 應用程式生命週期指南 ,進一步瞭解投放工作階段和網路接收端的生命週期。
課程由課程管理
SessionManager
、
讓應用程式透過
CastContext.getSessionManager()
。
個別工作階段是以類別的子類別表示
Session
。
例如:
CastSession
敬上
則代表有投放裝置的工作階段。您的應用程式可以存取目前有效的
透過以下方式投放工作階段:
SessionManager.getCurrentCastSession()
。
應用程式可使用
SessionManagerListener
敬上
類別,以便監控工作階段事件,例如建立、暫停、恢復和
終止。架構會自動嘗試從
工作階段執行時異常終止/意外終止。
系統會建立工作階段並自動移除,以回應使用者手勢
MediaRouter
對話方塊。
如要進一步瞭解 Cast 啟動錯誤,應用程式可以使用
CastContext#getCastReasonCodeForCastStatusCode(int)
敬上
將工作階段啟動錯誤轉換成
CastReasonCodes
。
請注意,部分工作階段啟動錯誤 (例如 CastReasonCodes#CAST_CANCELLED
)
為預期行為,且不應記錄為錯誤。
如果您需要掌握工作階段的狀態變更,
SessionManagerListener
。這個範例會監聽
Activity
中的 CastSession
。
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 inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { invalidateOptionsMenu() } override fun onSessionStartFailed(session: CastSession?, error: Int) { val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error) // Handle error } override fun onSessionSuspended(session: CastSession?, reason Int) {} override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { invalidateOptionsMenu() } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { finish() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession } 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 class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { invalidateOptionsMenu(); } @Override public void onSessionStartFailed(CastSession session, int error) { int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error); // Handle error } @Override public void onSessionSuspended(CastSession session, int reason) {} @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { invalidateOptionsMenu(); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { finish(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
變更串流裝置
保留工作階段狀態是串流傳輸的基礎, 使用者可以使用 Google Home 語音指令,切換不同裝置中的現有音訊和影片串流 應用程式或智慧螢幕。在一部裝置 (來源) 上停止播放媒體,然後於另一部裝置 ( 目的地)。凡是裝有最新韌體的投放裝置,都可以做為來源或目的地 變更串流裝置。
如要在變更串流或擴充串流期間取得新的目的地裝置,請按照下列步驟操作:
註冊
Cast.Listener
敬上
方法是使用
CastSession#addCastListener
。
然後呼叫
CastSession#getCastDevice()
敬上
在 onDeviceNameChanged
回呼期間執行這個動作
詳情請見 透過網路接收器轉移串流裝置 瞭解詳情
自動重新連線
架構提供了
ReconnectionService
敬上
傳送者應用程式可啟用這個 API 以處理重新連線
極端案例,例如:
- 在 Wi-Fi 連線暫時中斷的情況下復原
- 從裝置睡眠狀態復原
- 從背景執行復原程序
- 在應用程式當機時復原
這項服務預設為啟用,但可在
CastOptions.Builder
。
如果自動合併,這項服務可自動併入應用程式的資訊清單 已在 Gradle 檔案中啟用。
架構會在有媒體工作階段時啟動服務並停止服務 播放媒體工作階段結束時
媒體控制的運作方式
Cast 架構淘汰了
RemoteMediaPlayer
敬上
Cast 2.x 類別提供的類別,改用新類別
RemoteMediaClient
、
它為一組更加便利的 API 提供了相同的功能
不必傳入 GoogleApiClient
當您的應用程式建立
CastSession
敬上
Web Receiver 應用程式 (支援媒體命名空間)
架構會自動建立 RemoteMediaClient
;應用程式
對 CastSession
呼叫 getRemoteMediaClient()
方法,即可存取該函式
執行個體。
向網路接收端發出要求的所有 RemoteMediaClient
方法,
會傳回可用來追蹤該要求的 PendingResult 物件。
「RemoteMediaClient
」的例項應可由以下人員共用:
應用程式的多個部分,以及
例如永久的迷你控制器
通知服務。
為此,這個執行個體支援註冊多個
RemoteMediaClient.Listener
。
設定媒體中繼資料
MediaMetadata
敬上
類別代表您要投放的媒體項目相關資訊。
下列範例會建立一個電影的新 MediaMetadata 執行個體,並設定
標題、副標題和兩張圖片
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()) movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0)))) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0)))); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));
詳情請見 圖片選取功能 對您如何運用含有媒體中繼資料的圖片
載入媒體
應用程式可以載入媒體項目,如以下程式碼所示。首次使用
MediaInfo.Builder
敬上
可用媒體中繼資料建立
MediaInfo
執行個體。取得
RemoteMediaClient
敬上
目前的 CastSession
,然後將 MediaInfo
載入
RemoteMediaClient
。使用 RemoteMediaClient
即可播放、暫停等
控制在 Web Receiver 上執行的媒體播放器應用程式。
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build() val remoteMediaClient = mCastSession.getRemoteMediaClient() remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build(); RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());
另請參閱 使用媒體曲目。
4K 影片格式
如要查看媒體的影片格式,請使用
getVideoInfo()
敬上
取得目前的執行個體
VideoInfo
。
這個執行個體包含 HDR 電視格式的類型和顯示高度
以像素為單位4K 格式的變化版本是以常數表示
HDR_TYPE_*
。
對多部裝置發送遠端控制通知
當使用者投放內容時,連上相同網路的其他 Android 裝置將取得 方便他們控製播放內容的通知擁有裝置的所有人 如果收到通知,您可以在「設定」中為裝置關閉相關通知 應用程式 >Google Cast >顯示遙控器通知。 (通知中會包含「設定」應用程式的捷徑)。詳情請參閱 投放遙控器通知。
新增迷你控制器
根據 Cast Design 檢查清單、 傳送者應用程式應提供永久性控制項,稱為 mini 控制器 當使用者離開目前的內容網頁, 另一個部分迷你控制器提供顯眼的提醒 當前投放工作階段的使用者只要輕觸迷你控制器 使用者即可返回 Cast 全螢幕展開控制器檢視畫面。
這個架構提供自訂 View (MinControllerFragment) 在每個活動的版面配置檔案底部 小控制器
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
當傳送方應用程式播放影片或音訊直播時,SDK 會自動在播放/暫停按鈕旁顯示播放/停止按鈕 小控制器中
如要設定這個自訂檢視畫面的標題和副標題, 如要選擇按鈕,請參閱 自訂迷你控制器。
新增展開控制器
按照 Google Cast 設計檢查清單規定,傳送方應用程式必須提供已展開的 控制器 不同媒體展開的控制器是全螢幕版本 小控制器
Cast SDK 為展開的控制器提供一個小工具,稱為
ExpandedControllerActivity
。
這是一個抽象類別,您必須建立子類別,才能新增「投放」按鈕。
首先,請建立新的選單資源檔案,讓展開控制器提供 「投放」按鈕:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
建立可擴充 ExpandedControllerActivity
的新類別。
class ExpandedControlsActivity : ExpandedControllerActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.expanded_controller, menu) CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item) return true } }
public class ExpandedControlsActivity extends ExpandedControllerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.expanded_controller, menu); CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); return true; } }
現在,請在 application
標記的應用程式資訊清單中宣告新活動:
<application>
...
<activity
android:name=".expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
...
</application>
編輯 CastOptionsProvider
,將 NotificationOptions
和
CastMediaOptions
將目標活動設為新活動:
override fun getCastOptions(context: Context): CastOptions? { val notificationOptions = NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity::class.java.name) .build() val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name) .build() return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build() }
public CastOptions getCastOptions(Context context) { NotificationOptions notificationOptions = new NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) .build(); CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) .build(); return new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build(); }
更新 LocalPlayerActivity
loadRemoteMedia
方法,以顯示您的
遠端媒體載入時的新活動:
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { val remoteMediaClient = mCastSession?.remoteMediaClient ?: return remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java) startActivity(intent) remoteMediaClient.unregisterCallback(this) } }) remoteMediaClient.load( MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position.toLong()).build() ) }
private void loadRemoteMedia(int position, boolean autoPlay) { if (mCastSession == null) { return; } final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); if (remoteMediaClient == null) { return; } remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() { @Override public void onStatusUpdated() { Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class); startActivity(intent); remoteMediaClient.unregisterCallback(this); } }); remoteMediaClient.load(new MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position).build()); }
當傳送方應用程式播放影片或音訊直播時,SDK 會自動在播放/暫停按鈕旁顯示播放/停止按鈕 。
如要使用主題設定外觀,請選擇要顯示的按鈕。 並新增自訂按鈕,請參閱 自訂展開控制器。
音量控制項
架構會自動管理傳送端應用程式的磁碟區。架構 會自動同步處理傳送者與網路接收端應用程式,讓傳送者 UI 一律會回報網路接收端所指定的音量。
實體按鈕音量控制
在 Android 上,可透過傳送端裝置上的實體按鈕, 根據預設,凡是使用 Jelly Bean 以上版本。
Jelly Bean 之前實體按鈕的音量控制
如何使用實體音量鍵控制網路接收器裝置音量
Jelly Bean 以前的 Android 裝置,傳送者應用程式應覆寫
dispatchKeyEvent
敬上
,並呼叫
CastContext.onDispatchVolumeKeyEventBeforeJellyBean()
:
class MyActivity : FragmentActivity() { override fun dispatchKeyEvent(event: KeyEvent): Boolean { return (CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event)) } }
class MyActivity extends FragmentActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { return CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event); } }
在通知和螢幕鎖定畫面中加入媒體控制項
在 Android 裝置上,Google Cast 設計檢查清單規定傳送者應用程式必須符合下列條件:
通知
以及鎖定
螢幕、
傳送位置,但傳送者應用程式未聚焦。
架構提供了
MediaNotificationService
敬上
和
MediaIntentReceiver
目的是協助傳送者應用程式在通知和鎖定中建構媒體控制項
。
MediaNotificationService
會在傳送者投放時執行,並顯示
含有圖片縮圖和目前投放相關資訊的通知
項目、播放/暫停按鈕及停止按鈕。
MediaIntentReceiver
這個 BroadcastReceiver
會處理來自以下來源的使用者動作
然後根據通知內容
應用程式可透過螢幕鎖定畫面,設定通知和媒體控制項
NotificationOptions
。
您的應用程式可設定通知中顯示的控制項按鈕,以及
使用者輕觸通知時要開啟的 Activity
。如果動作
如未明確提供預設值
「MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
」和
系統將使用 MediaIntentReceiver.ACTION_STOP_CASTING
。
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". val buttonActions: MutableList<String> = ArrayList() buttonActions.add(MediaIntentReceiver.ACTION_REWIND) buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK) buttonActions.add(MediaIntentReceiver.ACTION_FORWARD) buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING) // Showing "play/pause" and "stop casting" in the compat view of the notification. val compatButtonActionsIndices = intArrayOf(1, 3) // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. val notificationOptions = NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity::class.java.name) .build()
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". List<String> buttonActions = new ArrayList<>(); buttonActions.add(MediaIntentReceiver.ACTION_REWIND); buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); buttonActions.add(MediaIntentReceiver.ACTION_FORWARD); buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); // Showing "play/pause" and "stop casting" in the compat view of the notification. int[] compatButtonActionsIndices = new int[]{1, 3}; // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. NotificationOptions notificationOptions = new NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity.class.getName()) .build();
已開啟通知和螢幕鎖定畫面的媒體控制項,已開啟
。如要停用,請呼叫
setNotificationOptions
敬上
含有空值
CastMediaOptions.Builder
。
目前,只要通知處於啟用狀態,系統就會開啟螢幕鎖定功能
已開啟。
// ... continue with the NotificationOptions built above val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build() val castOptions: CastOptions = Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build()
// ... continue with the NotificationOptions built above CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build(); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build();
當傳送方應用程式播放影片或音訊直播時,SDK 會自動在播放/暫停按鈕旁顯示播放/停止按鈕 而非鎖定螢幕控制項
注意:如要在搭載 Lollipop 之前版本的裝置上顯示螢幕鎖定控制項,
RemoteMediaClient
會自動代替你要求音訊焦點。
處理錯誤
傳送方應用程式必須處理所有錯誤回呼並判斷 Cast 生命週期各階段的最佳回應。應用程式可顯示 輸出錯誤或中斷連線 網路接收器。