Panduan developer ini menjelaskan cara menambahkan dukungan Google Cast ke Android Anda aplikasi pengirim menggunakan Android Sender SDK.
Perangkat seluler atau laptop adalah pengirim yang mengontrol pemutaran, dan Perangkat Google Cast adalah Penerima yang menampilkan konten di TV.
Framework pengirim mengacu pada biner library class Cast dan resource yang ada saat runtime pada pengirim. Aplikasi pengirim atau Aplikasi Cast mengacu pada aplikasi yang juga berjalan pada pengirim. Aplikasi Penerima Web merujuk pada aplikasi HTML yang berjalan di perangkat yang kompatibel untuk Cast.
Framework pengirim menggunakan desain callback asinkron untuk memberi tahu pengirim aplikasi peristiwa dan transisi antara berbagai status kehidupan aplikasi Cast siklus.
Alur aplikasi
Langkah-langkah berikut menjelaskan alur eksekusi tingkat tinggi yang umum untuk pengirim. Aplikasi Android:
- Framework Cast otomatis dimulai
MediaRouter
penemuan perangkat berdasarkan siklus prosesActivity
. - Saat pengguna mengklik tombol Cast, framework akan menampilkan Cast dialog dengan daftar perangkat Transmisi yang ditemukan.
- Saat pengguna memilih perangkat Cast, framework akan mencoba meluncurkan Aplikasi Penerima Web di perangkat Cast.
- Kerangka kerja memanggil callback di aplikasi pengirim untuk mengonfirmasi bahwa jaringan Aplikasi penerima diluncurkan.
- Kerangka kerja ini membuat saluran komunikasi antara pengirim dan Web Aplikasi penerima.
- Framework ini menggunakan saluran komunikasi untuk memuat dan mengontrol media pemutaran di Penerima Web.
- Kerangka kerja ini menyinkronkan status pemutaran media antara pengirim dan Penerima Web: saat pengguna melakukan tindakan UI pengirim, framework akan meneruskan permintaan kontrol media tersebut ke Penerima Web, dan ketika Penerima Web mengirimkan pembaruan status media, framework memperbarui status UI pengirim.
- Saat pengguna mengklik tombol Cast untuk memutuskan sambungan dari perangkat Cast, kerangka kerja akan memutuskan sambungan aplikasi pengirim dari Penerima Web.
Untuk daftar lengkap semua class, metode, dan peristiwa di Google Cast Android SDK, lihat Referensi Google Cast Sender API untuk Android. Bagian berikut membahas langkah-langkah untuk menambahkan Cast ke aplikasi Android.
Mengonfigurasi manifes Android
File AndroidManifest.xml di aplikasi mengharuskan Anda mengonfigurasi untuk SDK Cast:
uses-sdk
Menyetel level API Android minimum dan target yang didukung oleh Cast SDK. Saat ini, nilai minimumnya adalah API level 23 dan targetnya adalah Level API 34.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Setel tema aplikasi berdasarkan versi minimum Android SDK. Misalnya, jika
Anda tidak menerapkan tema sendiri, sebaiknya gunakan varian
Theme.AppCompat
saat menargetkan versi Android SDK minimum yang
sebelum versi Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
Melakukan inisialisasi Konteks Cast
Framework ini memiliki objek singleton global, CastContext
, yang mengoordinasikan
semua interaksi kerangka kerja.
Aplikasi Anda harus menerapkan
OptionsProvider
untuk menyediakan opsi yang diperlukan untuk menginisialisasi
CastContext
singleton. OptionsProvider
menyediakan instance
CastOptions
yang berisi opsi yang memengaruhi perilaku kerangka kerja. Paling sering
pentingnya adalah ID aplikasi Penerima
Web, yang digunakan untuk memfilter
hasil penemuan dan untuk meluncurkan aplikasi Penerima Web saat sesi Cast
memulai.
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; } }
Anda harus mendeklarasikan nama yang sepenuhnya memenuhi syarat dari OptionsProvider
yang diterapkan
sebagai kolom metadata di file AndroidManifest.xml di aplikasi pengirim:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext
diinisialisasi dengan lambat saat CastContext.getSharedInstance()
dipanggil.
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); } }
Widget UX Cast
Framework Cast menyediakan widget yang sesuai dengan Desain Cast {i>Checklist<i} (Daftar periksa):
Overlay Pengantar: Framework ini menyediakan View kustom,
IntroductoryOverlay
, yang ditampilkan kepada pengguna untuk menarik perhatian ke tombol Cast pertama kali penerima tersedia. Aplikasi Sender dapat menyesuaikan teks dan posisi judul teks.Tombol Cast: Tombol Cast terlihat terlepas dari ketersediaan perangkat Cast. Saat pengguna pertama kali mengklik tombol Cast, dialog Cast akan ditampilkan yang mencantumkan perangkat yang ditemukan. Saat pengguna mengklik tombol Cast saat perangkat terhubung, perangkat akan menampilkan metadata media saat ini (seperti judul, nama studio rekaman, dan gambar thumbnail) atau memungkinkan pengguna memutuskan sambungan dari perangkat Cast. "Tombol Cast" kadang-kadang mengacu pada sebagai "ikon Cast".
Pengontrol Mini: Saat pengguna mentransmisikan konten dan keluar dari konten saat ini laman konten atau pengontrol yang diperluas ke layar lain di aplikasi pengirim, pengontrol mini ditampilkan di bagian bawah layar untuk memungkinkan pengguna melihat metadata media yang sedang ditransmisikan dan untuk mengontrol pemutaran.
Pengontrol yang Diperluas: Saat pengguna mentransmisikan konten, jika mereka mengklik notifikasi media atau pengontrol mini, pengontrol yang diperluas akan diluncurkan, yang menampilkan metadata media yang sedang diputar dan menyediakan beberapa tombol untuk dikontrol pemutaran media.
Notifikasi: Khusus Android. Saat pengguna mentransmisikan konten dan keluar dari aplikasi pengirim, akan muncul notifikasi media yang menunjukkan proses transmisi metadata media dan kontrol pemutaran.
Layar Kunci: Khusus Android. Saat pengguna mentransmisikan konten dan melakukan navigasi (atau perangkat waktu habis) ke layar kunci, kontrol layar kunci media ditampilkan yang menunjukkan metadata media dan kontrol pemutaran yang sedang ditransmisikan.
Panduan berikut mencakup deskripsi cara menambahkan widget ini ke aplikasi Anda.
Menambahkan Tombol Cast
Android
MediaRouter
API didesain untuk mengaktifkan tampilan dan pemutaran media di perangkat sekunder.
Aplikasi Android yang menggunakan MediaRouter
API harus menyertakan tombol Cast sebagai bagian
antarmuka pengguna mereka, untuk memungkinkan pengguna
memilih rute media untuk memutar media
perangkat sekunder seperti perangkat Cast.
Kerangka kerja ini membuat penambahan
MediaRouteButton
sebagai
Cast button
sangatlah mudah. Anda harus menambahkan item menu atau MediaRouteButton
terlebih dahulu di xml
yang mendefinisikan menu Anda, dan menggunakan
CastButtonFactory
untuk menghubungkannya dengan kerangka kerja.
// 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; }
Lalu, jika Activity
Anda mewarisi dari
FragmentActivity
,
Anda dapat menambahkan
MediaRouteButton
ke tata letak Anda.
// 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); }
Untuk menyetel tampilan tombol Cast menggunakan tema, lihat Menyesuaikan Tombol Cast.
Mengonfigurasi penemuan perangkat
Penemuan perangkat sepenuhnya dikelola oleh
CastContext
Saat menginisialisasi CastContext, aplikasi pengirim menentukan Penerima Web
ID aplikasi, dan juga bisa meminta pemfilteran namespace dengan menetapkan
supportedNamespaces
inci
CastOptions
.
CastContext
menyimpan referensi ke MediaRouter
secara internal, dan akan dimulai
proses penemuan dalam kondisi berikut:
- Berdasarkan algoritma yang dirancang untuk menyeimbangkan latensi penemuan perangkat dan penggunaan baterai, penemuan terkadang akan dimulai secara otomatis saat aplikasi pengirim berada di latar depan.
- Dialog Cast terbuka.
- SDK Cast mencoba memulihkan sesi Transmisi.
Proses penemuan akan dihentikan saat dialog Transmisi ditutup atau aplikasi pengirim memasuki latar belakang.
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; } }
Cara kerja pengelolaan sesi
SDK Cast memperkenalkan konsep sesi Cast, jaringan yang menggabungkan langkah-langkah untuk menghubungkan ke perangkat, meluncurkan (atau bergabung) Aplikasi penerima, menghubungkan ke aplikasi tersebut, dan menginisialisasi saluran kontrol media. Lihat Penerima Web Panduan siklus proses aplikasi untuk informasi selengkapnya tentang sesi Cast dan siklus proses Penerima Web.
Sesi dikelola oleh kelas
SessionManager
,
yang dapat diakses oleh
aplikasi Anda melalui
CastContext.getSessionManager()
.
Sesi individual diwakili oleh subclass dari class
Session
Misalnya,
CastSession
mewakili sesi dengan perangkat Cast. Aplikasi Anda dapat mengakses data yang aktif saat ini
Sesi transmisi melalui
SessionManager.getCurrentCastSession()
Aplikasi Anda dapat menggunakan
SessionManagerListener
untuk memantau peristiwa sesi, seperti pembuatan, penangguhan, dimulainya kembali, dan
penghentian layanan. Kerangka kerja secara otomatis
mencoba melanjutkan dari
penghentian yang tidak normal/tiba-tiba saat sesi aktif.
Sesi dibuat dan dihapus secara otomatis sesuai dengan gestur pengguna
dari dialog MediaRouter
.
Untuk lebih memahami error saat memulai Cast, aplikasi dapat menggunakan
CastContext#getCastReasonCodeForCastStatusCode(int)
untuk mengonversi kesalahan
awal sesi menjadi
CastReasonCodes
.
Perhatikan bahwa beberapa error awal sesi (mis. CastReasonCodes#CAST_CANCELLED
)
adalah perilaku yang diinginkan dan
tidak boleh dicatat sebagai kesalahan.
Jika Anda perlu mengetahui perubahan status untuk sesi tersebut, Anda dapat menerapkan
SessionManagerListener
. Contoh ini mendengarkan ketersediaan
CastSession
di Activity
.
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); } }
Transfer streaming
Mempertahankan status sesi adalah dasar dari transfer streaming, dengan pengguna dapat memindahkan streaming audio dan video yang ada di seluruh perangkat menggunakan perintah suara, Google Home Aplikasi, atau layar smart. Media berhenti diputar di satu perangkat (sumber) dan berlanjut di perangkat lain ( tujuan). Setiap perangkat Cast dengan firmware terbaru dapat berfungsi sebagai sumber atau tujuan di atau transfer data.
Untuk mendapatkan perangkat tujuan baru selama transfer atau perluasan streaming,
daftarkan
Cast.Listener
menggunakan
CastSession#addCastListener
.
Lalu panggil
CastSession#getCastDevice()
selama callback onDeviceNameChanged
.
Lihat Transfer streaming di Penerima Web untuk informasi selengkapnya.
Penyambungan kembali otomatis
Kerangka kerja ini memberikan
ReconnectionService
yang dapat diaktifkan oleh aplikasi pengirim
untuk menangani koneksi kembali dalam banyak
pojok, seperti:
- Memulihkan koneksi WiFi sementara
- Pulihkan dari mode tidur perangkat
- Memulihkan aplikasi dengan latar belakang
- Memulihkan jika aplikasi mengalami error
Layanan ini diaktifkan secara default, dan dapat dinonaktifkan di
CastOptions.Builder
Layanan ini dapat otomatis digabungkan ke dalam manifes aplikasi jika penggabungan otomatis diaktifkan dalam file gradle Anda.
Framework akan memulai layanan saat ada sesi media, dan menghentikannya saat sesi media berakhir.
Cara kerja Kontrol Media
Framework Cast menghentikan penggunaan
RemoteMediaPlayer
dari Cast 2.x dan mendukung class baru
RemoteMediaClient
,
yang menyediakan fungsi yang sama dalam sekumpulan API yang lebih praktis, dan
sehingga tidak perlu meneruskan GoogleApiClient.
Saat aplikasi Anda membuat
CastSession
dengan aplikasi Penerima Web yang mendukung namespace media, yaitu instance
RemoteMediaClient
akan otomatis dibuat oleh framework; aplikasi Anda dapat
mengaksesnya dengan memanggil metode getRemoteMediaClient()
pada CastSession
di instance Compute Engine.
Semua metode RemoteMediaClient
yang mengirimkan permintaan ke Penerima Web akan
mengembalikan objek PendingResult yang dapat digunakan untuk melacak permintaan tersebut.
Diharapkan instance RemoteMediaClient
dapat dibagikan oleh
beberapa bagian aplikasi, dan tentu saja beberapa komponen internal
aplikasi, seperti pengontrol mini persisten dan
layanan notifikasi.
Untuk itu, instance ini mendukung pendaftaran beberapa instance
RemoteMediaClient.Listener
.
Menyetel metadata media
Tujuan
MediaMetadata
mewakili informasi tentang item media yang ingin Anda Transmisikan. Tujuan
contoh berikut membuat instance MediaMetadata baru dari sebuah film dan menyetel
judul, sub judul, dan dua gambar.
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))));
Lihat Pemilihan Gambar tentang penggunaan gambar dengan metadata media.
Muat media
Aplikasi Anda dapat memuat item media, seperti yang ditunjukkan pada kode berikut. Penggunaan pertama
MediaInfo.Builder
metadata media untuk membuat
MediaInfo
di instance Compute Engine. Dapatkan
RemoteMediaClient
dari CastSession
saat ini, lalu muat MediaInfo
ke dalam
RemoteMediaClient
. Gunakan RemoteMediaClient
untuk memutar, menjeda, dan lainnya
mengontrol aplikasi pemutar media yang
berjalan di Penerima Web.
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());
Lihat juga bagian tentang menggunakan trek media.
Format video 4K
Untuk memeriksa format video pada media Anda, gunakan
getVideoInfo()
di MediaStatus untuk mendapatkan instance
VideoInfo
.
Instance ini berisi jenis format TV HDR dan tinggi tampilan
dan lebarnya dalam piksel. Varian format 4K ditunjukkan oleh konstanta
HDR_TYPE_*
Notifikasi remote control ke beberapa perangkat
Ketika pengguna melakukan transmisi, perangkat Android lain di jaringan yang sama akan notifikasi untuk memungkinkan mereka mengontrol pemutaran. Siapa saja yang perangkatnya dapat menonaktifkannya untuk perangkat tersebut di Pengaturan di Google > Google Cast > Tampilkan notifikasi remote control. (Notifikasi tersebut menyertakan pintasan ke aplikasi Setelan.) Untuk detail selengkapnya, lihat Notifikasi remote control Cast.
Tambahkan pengontrol mini
Menurut Desain Cast, Checklist, aplikasi pengirim harus menyediakan kontrol persisten yang dikenal sebagai mini pengontrol yang akan muncul saat pengguna keluar dari halaman konten saat ini untuk bagian lain dari aplikasi pengirim. Pengontrol mini menyediakan pengingat yang terlihat kepada pengguna sesi Cast saat ini. Dengan mengetuk pengontrol mini, pengguna dapat kembali ke tampilan pengontrol yang diperluas layar penuh Cast.
Framework ini menyediakan View kustom, MiniControllerFragment, yang dapat Anda tambahkan ke bagian bawah file tata letak setiap aktivitas di mana Anda ingin menampilkan pengontrol mini.
<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" />
Saat aplikasi pengirim memutar live stream video atau audio, SDK otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di pengontrol mini.
Untuk mengatur tampilan teks dari judul dan sub judul dari tampilan khusus ini, dan untuk memilih tombol, lihat Sesuaikan Pengontrol Mini.
Tambahkan pengontrol yang diperluas
Checklist Desain Google Cast mengharuskan aplikasi pengirim menyediakan link yang diperluas pengontrol untuk media yang sedang Cast. Pengontrol yang diperluas adalah versi layar penuh dari pengontrol mini.
SDK Cast menyediakan widget untuk pengontrol yang diperluas yang disebut
ExpandedControllerActivity
Ini adalah class abstrak yang harus dibuat subclass untuk menambahkan tombol Cast.
Pertama, buat file sumber daya menu baru agar {i>controller<i} yang diperluas menyediakan tombol Cast:
<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>
Buat class baru yang memperluas 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; } }
Sekarang, deklarasikan aktivitas baru Anda di manifes aplikasi dalam tag 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>
Edit CastOptionsProvider
dan ubah NotificationOptions
, lalu
CastMediaOptions
untuk menetapkan aktivitas target ke aktivitas baru Anda:
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(); }
Perbarui metode LocalPlayerActivity
loadRemoteMedia
untuk menampilkan
aktivitas baru saat media jarak jauh dimuat:
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()); }
Saat aplikasi pengirim memutar live stream video atau audio, SDK otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda dalam pengontrol yang diperluas.
Untuk mengatur tampilan menggunakan tema, pilih tombol yang akan ditampilkan, dan menambahkan tombol khusus, lihat Sesuaikan Pengontrol yang Diperluas.
Kontrol volume
Framework ini akan otomatis mengelola volume untuk aplikasi pengirim. Kerangka kerja menyinkronkan aplikasi pengirim dan Penerima Web secara otomatis sehingga pengirim UI selalu melaporkan volume yang ditentukan oleh Penerima Web.
Kontrol volume tombol fisik
Di Android, tombol fisik di perangkat pengirim dapat digunakan untuk mengubah volume sesi Cast pada Penerima Web secara default untuk perangkat apa pun yang menggunakan Jelly Bean atau yang lebih baru.
Kontrol volume tombol fisik sebelum Jelly Bean
Untuk menggunakan tombol volume fisik guna mengontrol volume perangkat Penerima Web
Perangkat Android yang lebih lama dari Jelly Bean, aplikasi pengirim harus mengganti
dispatchKeyEvent
dalam Aktivitas mereka, dan memanggil
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); } }
Menambahkan kontrol media ke notifikasi dan layar kunci
Khusus Android, Checklist Desain Google Cast memerlukan aplikasi pengirim untuk
menerapkan kontrol media di
notifikasi
dan di kunci
layar,
di mana pengirim melakukan transmisi tetapi
aplikasi pengirim tidak memiliki fokus. Tujuan
menyediakan
MediaNotificationService
dan
MediaIntentReceiver
untuk membantu aplikasi pengirim membuat kontrol media dalam notifikasi dan di kunci
layar.
MediaNotificationService
berjalan saat pengirim melakukan transmisi, dan akan menampilkan
notifikasi dengan thumbnail gambar dan informasi tentang transmisi saat ini
tombol putar/jeda, dan tombol berhenti.
MediaIntentReceiver
adalah BroadcastReceiver
yang menangani tindakan pengguna dari
notifikasi.
Aplikasi Anda dapat mengonfigurasi notifikasi dan kontrol media dari layar kunci melalui
NotificationOptions
Aplikasi Anda dapat mengonfigurasi tombol kontrol apa yang akan ditampilkan di notifikasi, dan
Activity
mana yang akan dibuka saat notifikasi diketuk oleh pengguna. Tindakan if
tidak diberikan secara eksplisit, nilai {i>default<i},
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
dan
MediaIntentReceiver.ACTION_STOP_CASTING
akan digunakan.
// 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();
Menampilkan kontrol media dari notifikasi dan layar kunci diaktifkan oleh
dan dapat dinonaktifkan dengan memanggil
setNotificationOptions
dengan null di
CastMediaOptions.Builder
.
Saat ini, fitur layar kunci diaktifkan selama notifikasi
mengaktifkan fitur ini.
// ... 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();
Saat aplikasi pengirim memutar live stream video atau audio, SDK otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda pada kontrol notifikasi tetapi bukan kontrol layar kunci.
Catatan: Untuk menampilkan kontrol layar kunci di perangkat sebelum versi Lollipop,
RemoteMediaClient
akan otomatis meminta fokus audio atas nama Anda.
Menangani error
Sangat penting bagi aplikasi pengirim untuk menangani semua callback error dan memutuskan respons terbaik untuk setiap tahap siklus proses Cast. Aplikasi dapat menampilkan dialog {i>error <i}ke pengguna atau ia dapat memutuskan untuk memutus koneksi ke Penerima Web.