Panduan developer ini menjelaskan cara menambahkan dukungan Google Cast ke aplikasi pengirim Android Anda 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 terkait yang ada saat runtime di pengirim. Aplikasi pengirim atau aplikasi Cast mengacu pada aplikasi yang juga berjalan di pengirim. Aplikasi Penerima Web mengacu pada aplikasi HTML yang berjalan di perangkat yang kompatibel dengan Cast.
Framework pengirim menggunakan desain callback asinkron untuk memberi tahu aplikasi pengirim tentang peristiwa dan untuk bertransisi di antara berbagai status siklus proses aplikasi Cast.
Alur aplikasi
Langkah-langkah berikut menjelaskan alur eksekusi tingkat tinggi yang umum untuk aplikasi Android pengirim:
- Framework Cast otomatis memulai penemuan perangkat
MediaRouter
berdasarkan siklus prosesActivity
. - Saat pengguna mengklik tombol Cast, framework akan menampilkan dialog Cast dengan daftar perangkat Cast yang ditemukan.
- Saat pengguna memilih perangkat Cast, framework akan mencoba meluncurkan aplikasi Penerima Web di perangkat Cast.
- Framework memanggil callback di aplikasi pengirim untuk mengonfirmasi bahwa aplikasi Penerima Web diluncurkan.
- Framework ini membuat saluran komunikasi antara aplikasi pengirim dan Penerima Web.
- Framework ini menggunakan saluran komunikasi untuk memuat dan mengontrol pemutaran media di Penerima Web.
- Framework 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 saat Penerima Web mengirim pembaruan status media, framework akan memperbarui status UI pengirim.
- Saat pengguna mengklik tombol Cast untuk memutuskan koneksi dari perangkat Cast, framework akan memutuskan koneksi aplikasi pengirim dari Penerima Web.
Untuk mengetahui 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 Transmisi ke aplikasi Android Anda.
Mengonfigurasi manifes Android
File AndroidManifest.xml aplikasi Anda mengharuskan Anda mengonfigurasi elemen berikut untuk Cast SDK:
uses-sdk
Tetapkan level API Android minimum dan target yang didukung Cast SDK. Saat ini, minimumnya adalah API level 23 dan targetnya adalah API level 34.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Tetapkan tema aplikasi Anda berdasarkan versi Android SDK minimum. Misalnya, jika
tidak menerapkan tema Anda sendiri, Anda harus menggunakan varian
Theme.AppCompat
saat menargetkan versi Android SDK minimum yang
pra-Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
Melakukan inisialisasi Konteks Transmisi
Framework memiliki objek singleton global, CastContext
, yang mengoordinasikan
semua interaksi framework.
Aplikasi Anda harus mengimplementasikan antarmuka
OptionsProvider
untuk menyediakan opsi yang diperlukan guna melakukan inisialisasi
singleton
CastContext
. OptionsProvider
menyediakan instance
CastOptions
yang berisi opsi yang memengaruhi perilaku framework. Yang paling penting adalah ID aplikasi Penerima Web, yang digunakan untuk memfilter hasil penemuan dan meluncurkan aplikasi Penerima Web saat sesi Cast dimulai.
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 dalam file AndroidManifest.xml 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 secara 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 Transmisi
Framework Cast menyediakan widget yang mematuhi Checklist Desain Cast:
Overlay Pengantar: Framework menyediakan View kustom,
IntroductoryOverlay
, yang ditampilkan kepada pengguna untuk menarik perhatian ke tombol Cast saat penerima pertama kali tersedia. Aplikasi Pengirim dapat menyesuaikan teks dan posisi teks judul.Tombol Transmisi: Tombol Transmisi terlihat terlepas dari ketersediaan perangkat Transmisi. Saat pengguna pertama kali mengklik tombol Cast, dialog Cast akan ditampilkan yang mencantumkan perangkat yang ditemukan. Saat pengguna mengklik tombol Cast saat perangkat terhubung, tombol tersebut akan menampilkan metadata media saat ini (seperti judul, nama studio rekaman, dan gambar thumbnail) atau memungkinkan pengguna memutuskan koneksi dari perangkat Cast. "Tombol Transmisikan" terkadang disebut "Ikon transmisikan".
Pengontrol Mini: Saat pengguna mentransmisikan konten dan telah keluar dari halaman konten saat ini atau memperluas pengontrol ke layar lain di aplikasi pengirim, pengontrol mini akan ditampilkan di bagian bawah layar agar pengguna dapat melihat metadata media yang sedang ditransmisikan dan 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 mengontrol pemutaran media.
Notifikasi: Khusus Android. Saat pengguna mentransmisikan konten dan keluar dari aplikasi pengirim, notifikasi media akan ditampilkan yang menampilkan metadata media dan kontrol pemutaran yang sedang ditransmisikan.
Layar Kunci: Khusus Android. Saat pengguna mentransmisikan konten dan membuka (atau waktu tunggu perangkat berakhir) ke layar kunci, kontrol layar kunci media akan ditampilkan yang menampilkan metadata media dan kontrol pemutaran yang sedang ditransmisikan.
Panduan berikut menyertakan deskripsi cara menambahkan widget ini ke aplikasi Anda.
Menambahkan Tombol Transmisikan
API
MediaRouter
Android dirancang untuk mengaktifkan tampilan dan pemutaran media di perangkat sekunder.
Aplikasi Android yang menggunakan MediaRouter
API harus menyertakan tombol Cast sebagai bagian
dari antarmuka penggunanya, untuk memungkinkan pengguna memilih rute media guna memutar media di
perangkat sekunder seperti perangkat Cast.
Framework ini membuat penambahan
MediaRouteButton
sebagai
Cast button
sangat mudah. Anda harus menambahkan item menu atau MediaRouteButton
terlebih dahulu dalam file
xml yang menentukan menu, dan menggunakan
CastButtonFactory
untuk menghubungkannya dengan framework.
// 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; }
Kemudian, jika Activity
mewarisi dari
FragmentActivity
,
Anda dapat menambahkan
MediaRouteButton
ke tata letak.
// 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 menetapkan tampilan tombol Transmisikan menggunakan tema, lihat Menyesuaikan Tombol Transmisikan.
Mengonfigurasi penemuan perangkat
Penemuan perangkat sepenuhnya dikelola oleh
CastContext
.
Saat melakukan inisialisasi CastContext, aplikasi pengirim menentukan ID aplikasi
Penerima Web, dan secara opsional dapat meminta pemfilteran namespace dengan menetapkan
supportedNamespaces
di
CastOptions
.
CastContext
menyimpan referensi ke MediaRouter
secara internal, dan akan memulai
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 memasuki latar depan.
- Dialog Transmisi terbuka.
- Cast SDK mencoba memulihkan sesi Transmisi.
Proses penemuan akan dihentikan saat dialog Transmisi ditutup atau aplikasi pengirim masuk ke 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
Cast SDK memperkenalkan konsep sesi Cast, yang pembuatannya menggabungkan langkah-langkah untuk menghubungkan ke perangkat, meluncurkan (atau bergabung) aplikasi Penerima Web, menghubungkan ke aplikasi tersebut, dan melakukan inisialisasi saluran kontrol media. Lihat Panduan siklus proses aplikasi Penerima Web untuk mengetahui informasi selengkapnya tentang sesi Transmisi dan siklus proses Penerima Web.
Sesi dikelola oleh class
SessionManager
,
yang dapat diakses aplikasi Anda melalui
CastContext.getSessionManager()
.
Setiap sesi diwakili oleh subclass dari class
Session
.
Misalnya,
CastSession
mewakili sesi dengan perangkat Transmisi. Aplikasi Anda dapat mengakses sesi Cast
yang saat ini aktif melalui
SessionManager.getCurrentCastSession()
.
Aplikasi Anda dapat menggunakan
class SessionManagerListener
untuk memantau peristiwa sesi, seperti pembuatan, penangguhan, dimulainya kembali, dan
penghentian. Framework akan otomatis mencoba melanjutkan dari
penghentian abnormal/tiba-tiba saat sesi aktif.
Sesi dibuat dan dihapus secara otomatis sebagai respons terhadap gestur pengguna
dari dialog MediaRouter
.
Untuk lebih memahami error awal Cast, aplikasi dapat menggunakan
CastContext#getCastReasonCodeForCastStatusCode(int)
untuk mengonversi error awal sesi menjadi
CastReasonCodes
.
Perhatikan bahwa beberapa error awal sesi (misalnya CastReasonCodes#CAST_CANCELLED
)
adalah perilaku yang diinginkan dan tidak boleh dicatat sebagai error.
Jika perlu mengetahui perubahan status untuk sesi, Anda dapat menerapkan
SessionManagerListener
. Contoh ini memproses 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 transfer streaming, tempat pengguna dapat memindahkan streaming audio dan video yang ada di seluruh perangkat menggunakan perintah suara, Aplikasi Google Home, atau layar smart. Media berhenti diputar di satu perangkat (sumber) dan berlanjut di perangkat lain (tujuan). Semua perangkat Cast dengan firmware terbaru dapat berfungsi sebagai sumber atau tujuan dalam transfer streaming.
Untuk mendapatkan perangkat tujuan baru selama transfer atau perluasan streaming,
daftarkan
Cast.Listener
menggunakan
CastSession#addCastListener
.
Kemudian, panggil
CastSession#getCastDevice()
selama callback onDeviceNameChanged
.
Lihat Transfer streaming di Penerima Web untuk mengetahui informasi selengkapnya.
Koneksi ulang otomatis
Framework ini menyediakan
ReconnectionService
yang dapat diaktifkan oleh aplikasi pengirim untuk menangani koneksi ulang dalam banyak kasus
sudut halus, seperti:
- Memulihkan dari hilangnya Wi-Fi sementara
- Memulihkan dari mode tidur perangkat
- Memulihkan dari aplikasi yang berjalan di 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 Anda jika penggabungan otomatis diaktifkan di file gradle Anda.
Framework akan memulai layanan saat ada sesi media, dan menghentikannya saat sesi media berakhir.
Cara kerja Kontrol Media
Framework Cast tidak lagi menggunakan class
RemoteMediaPlayer
dari Cast 2.x dan menggunakan class baru
RemoteMediaClient
,
yang menyediakan fungsi yang sama dalam serangkaian API yang lebih praktis, dan
menghindari harus meneruskan GoogleApiClient.
Saat aplikasi Anda membuat
CastSession
dengan aplikasi Penerima Web yang mendukung namespace media, instance
RemoteMediaClient
akan otomatis dibuat oleh framework; aplikasi Anda dapat
mengaksesnya dengan memanggil metode getRemoteMediaClient()
pada instance
CastSession
.
Semua metode RemoteMediaClient
yang mengeluarkan permintaan ke Penerima Web akan menampilkan objek PendingResult yang dapat digunakan untuk melacak permintaan tersebut.
Instance RemoteMediaClient
diharapkan dapat dibagikan oleh
beberapa bagian aplikasi Anda, dan memang beberapa komponen internal
framework, seperti pengontrol mini persisten dan
layanan notifikasi.
Untuk itu, instance ini mendukung pendaftaran beberapa instance
RemoteMediaClient.Listener
.
Menetapkan metadata media
Class
MediaMetadata
mewakili informasi tentang item media yang ingin Anda Transmisikan. Contoh
berikut membuat instance MediaMetadata baru dari film dan menetapkan
judul, subtitel, 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.
Memuat media
Aplikasi Anda dapat memuat item media, seperti yang ditunjukkan dalam kode berikut. Pertama, gunakan
MediaInfo.Builder
dengan metadata media untuk membuat
instance
MediaInfo
. Dapatkan
RemoteMediaClient
dari CastSession
saat ini, lalu muat MediaInfo
ke dalam
RemoteMediaClient
tersebut. Gunakan RemoteMediaClient
untuk memutar, menjeda, dan 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 media Anda, gunakan
getVideoInfo()
di MediaStatus untuk mendapatkan instance
VideoInfo
saat ini.
Instance ini berisi jenis format TV HDR serta tinggi dan lebar
layar dalam piksel. Varian format 4K ditunjukkan oleh konstanta
HDR_TYPE_*
.
Mengontrol notifikasi dari jarak jauh ke beberapa perangkat
Saat pengguna melakukan transmisi, perangkat Android lain di jaringan yang sama akan mendapatkan notifikasi untuk mengontrol pemutaran juga. Siapa pun yang perangkatnya menerima notifikasi tersebut dapat menonaktifkannya untuk perangkat tersebut di aplikasi Setelan di Google > Google Cast > Tampilkan notifikasi kontrol jarak jauh. (Notifikasi menyertakan pintasan ke aplikasi Setelan.) Untuk mengetahui detail selengkapnya, lihat Mentransmisikan notifikasi kontrol jarak jauh.
Menambahkan pengontrol mini
Menurut Checklist Desain Cast, aplikasi pengirim harus menyediakan kontrol persisten yang dikenal sebagai pengontrol mini yang akan muncul saat pengguna keluar dari halaman konten saat ini ke bagian lain dari aplikasi pengirim. Pengontrol mini memberikan 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 tempat 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 akan otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di pengontrol mini.
Untuk menetapkan tampilan teks judul dan subtitel tampilan kustom ini, dan untuk memilih tombol, lihat Menyesuaikan Mini Controller.
Menambahkan pengontrol yang diperluas
Checklist Desain Google Cast mewajibkan aplikasi pengirim menyediakan pengontrol yang diperluas untuk media yang ditransmisikan. 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 resource menu baru agar pengontrol yang diperluas dapat menyediakan tombol Transmisikan:
<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
dan
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 akan otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di pengontrol yang diperluas.
Untuk menyetel tampilan menggunakan tema, memilih tombol yang akan ditampilkan, dan menambahkan tombol kustom, lihat Menyesuaikan Pengontrol yang Diperluas.
Kontrol volume
Framework ini secara otomatis mengelola volume untuk aplikasi pengirim. Framework akan otomatis menyinkronkan aplikasi pengirim dan Penerima Web sehingga UI pengirim 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 Transmisi di 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 di
perangkat Android yang lebih lama dari Jelly Bean, aplikasi pengirim harus mengganti
dispatchKeyEvent
di Aktivitasnya, 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 di Android, Checklist Desain Google Cast mewajibkan aplikasi pengirim untuk
menerapkan kontrol media dalam
notifikasi
dan di layar
kunci,
tempat pengirim melakukan transmisi, tetapi aplikasi pengirim tidak memiliki fokus. Framework
ini menyediakan
MediaNotificationService
dan
MediaIntentReceiver
untuk membantu aplikasi pengirim membuat kontrol media dalam notifikasi dan di layar
kunci.
MediaNotificationService
berjalan saat pengirim melakukan transmisi, dan akan menampilkan
notifikasi dengan thumbnail gambar dan informasi tentang item 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 yang akan ditampilkan dalam notifikasi, dan
Activity
yang akan dibuka saat notifikasi diketuk oleh pengguna. Jika tindakan
tidak diberikan secara eksplisit, nilai default,
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 secara
default, dan dapat dinonaktifkan dengan memanggil
setNotificationOptions
dengan null di
CastMediaOptions.Builder
.
Saat ini, fitur layar kunci diaktifkan selama notifikasi
diaktifkan.
// ... 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 akan otomatis menampilkan tombol putar/berhenti sebagai pengganti tombol putar/jeda di kontrol notifikasi, tetapi tidak di kontrol layar kunci.
Catatan: Untuk menampilkan kontrol layar kunci di perangkat pra-Lollipop,
RemoteMediaClient
akan otomatis meminta fokus audio untuk Anda.
Menangani error
Sangat penting bagi aplikasi pengirim untuk menangani semua callback error dan menentukan respons terbaik untuk setiap tahap siklus proses Transmisi. Aplikasi dapat menampilkan dialog error kepada pengguna atau dapat memutuskan untuk memutuskan koneksi ke Penerima Web.