1. סקירה כללית
בשיעור הזה תלמדו איך לשנות אפליקציית וידאו קיימת ל-Android כדי להעביר תוכן למכשיר שתומך ב-Google Cast.
מה זה Google Cast?
Google Cast מאפשר למשתמשים להעביר (cast) תוכן ממכשיר נייד לטלוויזיה. המשתמשים יוכלו להשתמש בנייד שלהם כשלט רחוק להפעלת מדיה בטלוויזיה.
באמצעות Google Cast SDK ניתן להרחיב את האפליקציה לשליטה בטלוויזיה או במערכת קול. ה-SDK של Cast מאפשר להוסיף את רכיבי ממשק המשתמש הנחוצים על סמך רשימת המשימות לעיצוב Google Cast.
רשימת המשימות לעיצוב Google Cast מסופקת כדי להפוך את חוויית המשתמש ב-Cast פשוטה וצפויה בכל הפלטפורמות הנתמכות.
מה אנחנו מתכוונים לבנות?
בסיום ה-Codelab, תהיה לך אפליקציית וידאו ב-Android שתאפשר להעביר סרטונים למכשיר שתומך ב-Google Cast.
מה תלמדו
- איך להוסיף את Google Cast SDK לאפליקציית וידאו לדוגמה.
- כיצד להוסיף את לחצן הפעלת Cast כדי לבחור מכשיר Google Cast.
- איך מתחברים למכשיר CAST ומפעילים מקלט מדיה
- איך מעבירים סרטון.
- איך להוסיף שלט רחוק מסוג Cast Mini לאפליקציה.
- איך לתמוך בהתראות מדיה ובפקדים של מסך הנעילה.
- איך מוסיפים שלט רחוק מורחב.
- כיצד להוסיף שכבת-על שמשמשת כמבוא.
- איך להתאים אישית ווידג'טים של Cast.
- איך לשלב את Cast Connect
מה הדרישות כדי להצטרף לתוכנית?
- Android SDK העדכני ביותר.
- Android Studio בגרסה 3.2 ואילך
- מכשיר נייד אחד עם Android מגרסה 4.1 ואילך Jelly Bean (רמת API 16).
- כבל נתונים בחיבור USB לחיבור המכשיר הנייד למחשב הפיתוח.
- מכשיר Google Cast, כמו Chromecast או Android TV, שמוגדר עם גישה לאינטרנט.
- טלוויזיה או צג עם יציאת HDMI.
- נדרש מכשיר Chromecast with Google TV כדי לבדוק את השילוב של Cast Connect, אבל הוא אופציונלי בשאר ה-Codelab. אם אין לכם חשבון Google, אפשר לדלג על השלב הוספת תמיכה ב-Cast Connect לקראת סוף המדריך.
ניסיון
- נדרש ידע קודם בפיתוח ב-Kotlin וב-Android.
- נדרש גם ידע קודם בצפייה בטלוויזיה :)
איך ייעשה שימוש במדריך הזה?
איזה דירוג מגיע לדעתך לחוויה שלך בבניית אפליקציות ל-Android?
איזה דירוג מגיע לדעתך לחוויית הצפייה בטלוויזיה?
2. לקבלת הקוד לדוגמה
אפשר להוריד את כל הקוד לדוגמה למחשב...
ופורקים את קובץ ה-ZIP שהורד.
3. הפעלת האפליקציה לדוגמה
קודם נראה איך נראית האפליקציה לדוגמה שהושלמה. האפליקציה היא נגן וידאו בסיסי. המשתמשים יכולים לבחור סרטון מתוך רשימה ואז להפעיל את הסרטון באופן מקומי במכשיר או להעביר (cast) אותו למכשיר Google Cast.
אחרי שמורידים את הקוד, ההוראות הבאות מתארות איך לפתוח ולהפעיל ב-Android Studio את האפליקציה לדוגמה שהושלמה:
בוחרים באפשרות ייבוא פרויקט במסך הפתיחה או באפשרויות התפריט קובץ > חדש > ייבוא פרויקט....
בוחרים את הספרייה app-done
מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.
לוחצים על קובץ > סנכרון פרויקט עם קובצי Gradle.
הפעל ניפוי באגים ב-USB במכשיר Android – ב-Android מגרסה 4.2 ואילך, מסך האפשרויות למפתחים מוסתר כברירת מחדל. כדי שהוא יוצג, עוברים אל הגדרות > מידע על הטלפון ומקישים שבע פעמים על מספר Build. חוזרים למסך הקודם, עוברים אל מערכת > אפשרויות מתקדמות ומקישים על אפשרויות למפתחים ליד תחתית המסך, ולאחר מכן מקישים על ניפוי באגים ב-USB כדי להפעיל את האפשרות.
מחברים את מכשיר Android ולוחצים על הלחצן הפעלה ב-Android Studio. אפליקציית הווידאו העברת סרטונים אמורה להופיע אחרי כמה שניות.
לוחצים על לחצן הפעלת Cast באפליקציית הווידאו ובוחרים את מכשיר ה-Google Cast.
בוחרים סרטון ולוחצים על לחצן ההפעלה.
הסרטון יתחיל לפעול במכשיר Google Cast.
יוצג הבקר המורחב. אפשר להשתמש בלחצן 'הפעלה'/'השהיה' כדי לשלוט בהפעלה.
חוזרים לרשימת הסרטונים.
מיני שלט רחוק עכשיו גלוי בחלק התחתון של המסך.
לוחצים על לחצן ההשהיה במיני-בקר כדי להשהות את הסרטון במכשיר הטלוויזיה. כדי להמשיך בהפעלת הסרטון, לוחצים על לחצן ההפעלה במיני-בקר.
לוחצים על לחצן דף הבית בנייד. אם למשוך למטה את ההתראות, אמורה להופיע התראה לגבי פעילות ההעברה.
נועלים את הטלפון וכשמבטלים את הנעילה, אמורה להופיע התראה במסך הנעילה כדי לשלוט בהפעלה של מדיה או להפסיק את ההעברה.
חוזרים לאפליקציית הווידאו ולוחצים על לחצן הפעלת Cast כדי להפסיק את ההעברה למכשיר Google Cast.
שאלות נפוצות
4. הכנת הפרויקט להתחלה
אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציה ההתחלתית שהורדת. הנה כמה מהמונחים של Google Cast שבהם נשתמש ב-Codelab זה:
- אפליקציית שולח פועלת במכשיר נייד או במחשב נייד,
- אפליקציית מקלט פועלת במכשיר Google Cast.
עכשיו אתם מוכנים להשתמש ב-Android Studio כדי להוסיף ולבנות פרויקטים חדשים באמצעות Android Studio:
- בוחרים את הספרייה
app-start
מהורדת הקוד לדוגמה (בוחרים באפשרות ייבוא פרויקט במסך הפתיחה או באפשרות התפריט קובץ > חדש > ייבוא פרויקט...). - לוחצים על הלחצן Sync Project with Gradle Files (סנכרון הפרויקט עם קובצי Gradle).
- לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה ולבחון את ממשק המשתמש.
עיצוב אפליקציות
האפליקציה מאחזרת רשימה של סרטונים משרת אינטרנט מרוחק ומספקת רשימה שהמשתמש יכול לעיין בה. המשתמשים יכולים לבחור סרטון כדי לראות את הפרטים שלו או להפעיל אותו במכשיר הנייד.
האפליקציה מורכבת משתי פעילויות עיקריות: VideoBrowserActivity
ו-LocalPlayerActivity
. כדי לשלב פונקציונליות של Google Cast, הפעילויות צריכות לעבור בירושה מה-AppCompatActivity
או מההורה FragmentActivity
. ההגבלה הזו קיימת כי נצטרך להוסיף את MediaRouteButton
(שסופק בספריית התמיכה של MediaRouter) כ-MediaRouteActionProvider
, והיא תעבוד רק אם הפעילות עוברת בירושה מהמחלקות שהוזכרו למעלה. ספריית התמיכה של MediaRouter תלויה בספריית התמיכה של AppCompat המספקת את המחלקות הנדרשות.
VideoBrowserActivity
הפעילות הזו מכילה Fragment
(VideoBrowserFragment
). הרשימה הזו מגובה בקובץ ArrayAdapter
(VideoListAdapter
). רשימת הסרטונים והמטא-נתונים המשויכים אליהם מתארחים בשרת מרוחק כקובץ JSON. מערכת AsyncTaskLoader
(VideoItemLoader
) מאחזרת את ה-JSON הזה ומעבדת אותו כדי ליצור רשימה של MediaItem
אובייקטים.
אובייקט MediaItem
יוצר מודל של סרטון ואת המטא-נתונים המשויכים אליו, למשל: השם, התיאור, כתובת ה-URL של השידור, כתובת ה-URL של התמונות המשניות וטראקים של טקסט שמשויך אליו (לכתוביות), אם יש כאלה. האובייקט MediaItem
מועבר בין פעילויות, ולכן יש ל-MediaItem
שיטות עזר להמיר אותו ל-Bundle
ולהפך.
כשהטוען בונה את הרשימה של MediaItems
, הוא מעביר את הרשימה הזו ל-VideoListAdapter
, שלאחר מכן מציג את הרשימה MediaItems
ב-VideoBrowserFragment
. למשתמש מוצגת רשימה של תמונות ממוזערות של סרטונים עם תיאור קצר של כל סרטון. כשבוחרים פריט, הערך של MediaItem
התואם מומר ל-Bundle
ומועבר ל-LocalPlayerActivity
.
LocalPlayerActivity
פעילות זו מציגה את המטא נתונים לגבי סרטון מסוים ומאפשרת למשתמש להפעיל את הסרטון באופן מקומי במכשיר הנייד.
הפעילות מארחת VideoView
, חלק מפקדי המדיה ואזור טקסט שבו אפשר להציג את תיאור הסרטון שנבחר. הנגן מכסה את החלק העליון של המסך ונשאר מקום לתיאור מפורט של הסרטון מתחתיו. המשתמשים יכולים להפעיל או להשהות סרטונים, או לבקש הפעלה מקומית של הסרטונים.
יחסי תלות
אנחנו משתמשים ב-AppCompatActivity
, לכן אנחנו צריכים את ספריית התמיכה של AppCompat. כדי לנהל את רשימת הסרטונים ולקבל את התמונות של הרשימה באופן אסינכרוני, אנחנו משתמשים בספריית Volley.
שאלות נפוצות
5. הוספת לחצן הפעלת Cast
אפליקציה שתומכת ב-Cast מציגה את לחצן הפעלת Cast בכל אחת מהפעילויות שלה. לחיצה על לחצן הפעלת Cast מציגה רשימה של מכשירי Cast שהמשתמש יכול לבחור. אם המשתמש הפעיל תוכן באופן מקומי במכשיר השולח, בחירה במכשיר CAST מפעילה או מחדש את ההפעלה במכשיר ה-CAST הזה. המשתמש יכול ללחוץ על לחצן הפעלת Cast בכל שלב במהלך ההעברה, ולהפסיק את ההעברה של האפליקציה למכשיר ה-Cast. למשתמש צריכה להיות אפשרות להתחבר למכשיר ה-Cast או להתנתק ממנו במהלך כל פעילות באפליקציה, כפי שמתואר ברשימת המשימות לעיצוב Google Cast.
יחסי תלות
מעדכנים את הקובץ build.gradle של האפליקציה כך שיכלול את יחסי התלות הנחוצים של הספרייה:
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'androidx.mediarouter:mediarouter:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.gms:play-services-cast-framework:21.1.0'
implementation 'com.android.volley:volley:1.2.1'
implementation "androidx.core:core-ktx:1.8.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
צריך לסנכרן את הפרויקט כדי לאשר את גרסאות ה-build של הפרויקט ללא שגיאות.
אתחול
מסגרת ההעברה (cast) כוללת אובייקט גלובלי מסוג singleton, ה-CastContext
, שמתאם את כל האינטראקציות של ההעברה.
צריך להטמיע את הממשק של OptionsProvider
כדי לספק CastOptions
שנדרש כדי לאתחל את הסינגלטון CastContext
. האפשרות החשובה ביותר היא מזהה האפליקציה של המקבל. המזהה הזה משמש לסינון תוצאות החיפוש של מכשירי CAST ולהפעלת האפליקציה המקבלת כשמתחילים סשן העברה.
אם אתם מפתחים אפליקציה שתומכת ב-Cast, תצטרכו להירשם כמפתח/ת Cast ולאחר מכן לקבל מזהה אפליקציה לאפליקציה שלכם. לצורך ה-Codelab הזה נשתמש במזהה אפליקציה לדוגמה.
מוסיפים את קובץ CastOptionsProvider.kt
החדש הבא לחבילת com.google.sample.cast.refplayer
של הפרויקט:
package com.google.sample.cast.refplayer
import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider
class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.build()
}
override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
}
עכשיו צריך להצהיר על OptionsProvider
בתוך התג "application
" בקובץ AndroidManifest.xml
של האפליקציה:
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />
צריך לאתחל באופן מדורג את CastContext
בשיטת VideoBrowserActivity
onCreate:
import com.google.android.gms.cast.framework.CastContext
private var mCastContext: CastContext? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastContext = CastContext.getSharedInstance(this)
}
צריך להוסיף את אותה לוגיקת אתחול ל-LocalPlayerActivity
.
לחצן הפעלת Cast
עכשיו, לאחר אתחול של CastContext
, עלינו להוסיף את לחצן הפעלת Cast כדי לאפשר למשתמש לבחור מכשיר CAST. לחצן הפעלת Cast מיושם על ידי MediaRouteButton
מספריית התמיכה MediaRouter. בדומה לכל סמל פעולה שאפשר להוסיף לפעילות (באמצעות ActionBar
או Toolbar
), קודם צריך להוסיף לתפריט את האפשרות המתאימה.
יש לערוך את הקובץ res/menu/browse.xml
ולהוסיף את הפריט MediaRouteActionProvider
בתפריט לפני פריט ההגדרות:
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
ניתן להחליף את השיטה onCreateOptionsMenu()
של VideoBrowserActivity
על ידי שימוש ב-CastButtonFactory
כדי לחבר את MediaRouteButton
ל-Cast ל-framework:
import com.google.android.gms.cast.framework.CastButtonFactory
private var mediaRouteMenuItem: MenuItem? = null
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.browse, menu)
mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu,
R.id.media_route_menu_item)
return true
}
שינוי onCreateOptionsMenu
ב-LocalPlayerActivity
באופן דומה.
לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. לחצן הפעלת Cast אמור להופיע בסרגל הפעולות של האפליקציה, ולאחר לחיצה עליו יוצג רשימה של מכשירי ה-Cast ברשת המקומית שלך. גילוי המכשירים מנוהל באופן אוטומטי על ידי CastContext
. עליך לבחור את מכשיר ה-Cast שלך, והאפליקציה של המקבל לדוגמה תיטען במכשיר ה-Cast. אפשר לנווט בין פעילות הגלישה לבין פעילות הנגן המקומי, והמצב של לחצן הפעלת Cast נשאר מסונכרן.
לא מצאנו תמיכה בהפעלת מדיה, ולכן עדיין אין אפשרות להפעיל סרטונים במכשיר Cast. לחץ על לחצן הפעלת Cast כדי להתנתק.
6. העברה (cast) של תוכן וידאו
נרחיב את האפליקציה לדוגמה כך שגם תפעיל סרטונים מרחוק במכשיר CAST. לשם כך, עלינו להאזין לאירועים השונים שנוצרו על ידי מסגרת ההעברה.
העברה (cast) של מדיה
ככלל, כדי להפעיל מדיה במכשיר CAST, צריך לבצע את הפעולות הבאות:
- יוצרים אובייקט
MediaInfo
ליצירת מודל של פריט מדיה. - עליך לחבר את מכשיר ה-Cast ולהפעיל את אפליקציית המקבל.
- טוענים את האובייקט
MediaInfo
במכשיר המקבל ומפעילים את התוכן. - מעקב אחר סטטוס המדיה.
- שליחת פקודות הפעלה לנמען על סמך האינטראקציות של המשתמש.
כבר ערכנו את שלב 2 בסעיף הקודם. קל לעשות את שלב 3 באמצעות מסגרת ההעברה. שלב 1 זהה למיפוי אובייקט אחד לאובייקט אחר; MediaInfo
הוא משהו ש-Google Cast מבינה, ו-MediaItem
הוא האנקפסולציה של האפליקציה שלנו לפריט מדיה. אנחנו יכולים למפות בקלות MediaItem
ל-MediaInfo
.
האפליקציה לדוגמה LocalPlayerActivity
כבר מבחינה בין הפעלה מקומית להפעלה מרחוק באמצעות טיפוסים בני מנייה (enum):
private var mLocation: PlaybackLocation? = null
enum class PlaybackLocation {
LOCAL, REMOTE
}
enum class PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE
}
ב-Codelab הזה לא חשוב שתבינו בדיוק איך פועל כל הלוגיקה של הנגן לדוגמה. חשוב להבין שיהיה צורך לשנות את נגן המדיה של האפליקציה כדי שתהיה מודע לשני מיקומי ההפעלה באופן דומה.
נכון לעכשיו, הנגן המקומי תמיד נמצא במצב הפעלה מקומי כי הוא עדיין לא יודע דבר על מצבי ההעברה. אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי מצבים שמתרחשים במסגרת Cast. לדוגמה, אם אנחנו מתחילים בהעברה (cast), עלינו להפסיק את ההפעלה באופן מקומי ולהשבית חלק מהפקדים. באופן דומה, אם מפסיקים את ההעברה (cast) במהלך הפעילות הזו, צריך לעבור להפעלה מקומית. כדי לטפל בכך, אנחנו צריכים להאזין לאירועים השונים שנוצרו על ידי מסגרת ההעברה (cast).
ניהול פעילות CAST
במסגרת ההעברה, תהליך ההעברה משלב את שלבי ההתחברות למכשיר, הפעלה (או הצטרפות), התחברות לאפליקציה של המקבל והפעלת ערוץ בקרת מדיה, אם זה רלוונטי. הערוץ לבקרת המדיה הוא האופן שבו מסגרת ההעברה שולחת ומקבלת הודעות מנגן המדיה של המקבל.
סשן ההעברה יתחיל באופן אוטומטי כשהמשתמש יבחר מכשיר מלחצן הפעלת Cast, ותופסק באופן אוטומטי כשהמשתמש יתנתק. גם חיבור מחדש לסשן של מקלט עקב בעיות ברשת מטופל באופן אוטומטי על ידי Cast SDK.
עכשיו נוסיף SessionManagerListener
לLocalPlayerActivity
:
import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...
private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...
private fun setupCastListener() {
mSessionManagerListener = object : SessionManagerListener<CastSession> {
override fun onSessionEnded(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
onApplicationConnected(session)
}
override fun onSessionResumeFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarted(session: CastSession, sessionId: String) {
onApplicationConnected(session)
}
override fun onSessionStartFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarting(session: CastSession) {}
override fun onSessionEnding(session: CastSession) {}
override fun onSessionResuming(session: CastSession, sessionId: String) {}
override fun onSessionSuspended(session: CastSession, reason: Int) {}
private fun onApplicationConnected(castSession: CastSession) {
mCastSession = castSession
if (null != mSelectedMedia) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView!!.pause()
loadRemoteMedia(mSeekbar!!.progress, true)
return
} else {
mPlaybackState = PlaybackState.IDLE
updatePlaybackLocation(PlaybackLocation.REMOTE)
}
}
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
private fun onApplicationDisconnected() {
updatePlaybackLocation(PlaybackLocation.LOCAL)
mPlaybackState = PlaybackState.IDLE
mLocation = PlaybackLocation.LOCAL
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
}
}
בפעילות של LocalPlayerActivity
נרצה לקבל הודעה כשנתחבר או נתנתק ממכשיר ה-CAST כדי שנוכל לעבור לנגן המקומי או ממנו. הערה: הקישוריות יכולה גם להיות משובשת לא רק על ידי המופע של האפליקציה שפועלת במכשיר הנייד, אלא גם על ידי מופע אחר של אפליקציה (או של אפליקציה אחרת) שפועל במכשיר נייד אחר.
הסשן הפעיל הנוכחי נגיש בתור SessionManager.getCurrentSession()
. סשנים נוצרים ומבוטלים באופן אוטומטי בתגובה לאינטראקציות של המשתמשים עם תיבות הדו-שיח 'העברה'.
אנחנו צריכים לרשום את ה-session listener שלנו ולאתחל כמה משתנים שבהם נשתמש בפעילות. שינוי השיטה LocalPlayerActivity
onCreate
ל:
import com.google.android.gms.cast.framework.CastContext
...
private var mCastContext: CastContext? = null
...
override fun onCreate(savedInstanceState: Bundle?) {
...
mCastContext = CastContext.getSharedInstance(this)
mCastSession = mCastContext!!.sessionManager.currentCastSession
setupCastListener()
...
loadViews()
...
val bundle = intent.extras
if (bundle != null) {
....
if (shouldStartPlayback) {
....
} else {
if (mCastSession != null && mCastSession!!.isConnected()) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
mPlaybackState = PlaybackState.IDLE
updatePlayButton(mPlaybackState)
}
}
...
}
המדיה בטעינה
ב-Cast SDK, RemoteMediaClient
מספק קבוצה של ממשקי API נוחים לניהול ההפעלה מרחוק של מדיה במכשיר המקבל. עבור CastSession
שתומך בהפעלת מדיה, ייווצר באופן אוטומטי מופע של RemoteMediaClient
על ידי ה-SDK. אפשר לגשת אליה על ידי קריאה לשיטה getRemoteMediaClient()
במכונה של CastSession
. צריך להוסיף את השיטות הבאות אל LocalPlayerActivity
כדי לטעון את הסרטון הנוכחי שנבחר במכשיר המקבל:
import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
remoteMediaClient.load( MediaLoadRequestData.Builder()
.setMediaInfo(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
private fun buildMediaInfo(): MediaInfo? {
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
return mSelectedMedia!!.url?.let {
MediaInfo.Builder(it)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("videos/mp4")
.setMetadata(movieMetadata)
.setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
.build()
}
}
עכשיו צריך לעדכן כמה שיטות קיימות כדי להשתמש בלוגיקת ההעברה (cast) לצורך תמיכה בהפעלה מרחוק:
private fun play(position: Int) {
startControllersTimer()
when (mLocation) {
PlaybackLocation.LOCAL -> {
mVideoView!!.seekTo(position)
mVideoView!!.start()
}
PlaybackLocation.REMOTE -> {
mPlaybackState = PlaybackState.BUFFERING
updatePlayButton(mPlaybackState)
//seek to a new position within the current media item's new position
//which is in milliseconds from the beginning of the stream
mCastSession!!.remoteMediaClient?.seek(position.toLong())
}
else -> {}
}
restartTrickplayTimer()
}
private fun togglePlayback() {
...
PlaybackState.IDLE -> when (mLocation) {
...
PlaybackLocation.REMOTE -> {
if (mCastSession != null && mCastSession!!.isConnected) {
loadRemoteMedia(mSeekbar!!.progress, true)
}
}
else -> {}
}
...
}
override fun onPause() {
...
mCastContext!!.sessionManager.removeSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
Log.d(TAG, "onResume() was called")
mCastContext!!.sessionManager.addSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
if (mCastSession != null && mCastSession!!.isConnected) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
super.onResume()
}
בשיטה updatePlayButton
, משנים את הערך של המשתנה isConnected
:
private fun updatePlayButton(state: PlaybackState?) {
...
val isConnected = (mCastSession != null
&& (mCastSession!!.isConnected || mCastSession!!.isConnecting))
...
}
עכשיו צריך ללחוץ על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. מתחברים למכשיר ה-Cast ומתחילים להפעיל סרטון. הסרטון אמור לפעול במכשיר המקבל.
7. מיני שלט רחוק
רשימת המשימות לעיצוב Cast מחייבת שכל אפליקציית Cast תספק בקר מיני שמופיע כשהמשתמש יוצא מדף התוכן הנוכחי. המיני-בקר מספק גישה מיידית ותזכורת גלויה להפעלת ההעברה הנוכחית.
ערכת Cast SDK מספקת תצוגה מותאמת אישית, MiniControllerFragment
, שניתן להוסיף לקובץ הפריסה של האפליקציה שמכילה את הפעילויות שבהן רוצים להציג את המיני-בקר.
צריך להוסיף את הגדרת המקטע הבאה לתחתית של res/layout/player_activity.xml
ושל res/layout/video_browser.xml
:
<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"/>
לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה ולהעביר סרטון. כשההפעלה תתחיל במכשיר, המיני שלט רחוק אמור להופיע בחלק התחתון של כל פעילות. אפשר לשלוט בהפעלה מרחוק באמצעות המיני-בקר. אם מנווטים בין פעילות הגלישה לבין פעילות הנגן המקומי, המצב של המיני-בקר אמור להישאר מסונכרן עם סטטוס ההפעלה של המדיה של המקבל.
8. התראות ומסך נעילה
רשימת המשימות לעיצוב Google Cast מחייבת אפליקציה לשולח להטמיע פקדי מדיה מהתראה וממסך הנעילה.
Cast SDK מספק MediaNotificationService
כדי לעזור לאפליקציית השולח ליצור פקדי מדיה להתראות ולמסך הנעילה. השירות ממוזג באופן אוטומטי למניפסט של האפליקציה באמצעות gradle.
MediaNotificationService
יפעל ברקע כשהשולח מעביר תוכן. כמו כן, תוצג בו התראה עם תמונה ממוזערת של תמונה ומטא-נתונים לגבי פריט ההעברה הנוכחי, לחצן הפעלה/השהיה ולחצן עצירה.
ניתן להפעיל את פקדי ההתראות ומסך הנעילה באמצעות CastOptions
כאשר מאתחלים את CastContext
. פקדי המדיה להתראות ולמסך הנעילה מופעלים כברירת מחדל. תכונת מסך הנעילה מופעלת כל עוד ההודעה פועלת.
עורכים את CastOptionsProvider
ומשנים את ההטמעה של getCastOptions
כך שיתאימו לקוד הזה:
import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.NotificationOptions
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(VideoBrowserActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build()
}
לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. מעבירים סרטון ויוצאים מהאפליקציה לדוגמה. אמורה להופיע התראה לגבי הסרטון שמופעל עכשיו במכשיר המקלט. לאחר נעילת המכשיר הנייד, ומסך הנעילה אמור להציג עכשיו פקדים להפעלת המדיה במכשיר ה-Cast.
9. שכבת-על למתחילים
לפי רשימת המשימות לעיצוב Google Cast, עליך להתקין אפליקציית שולח להציג את לחצן הפעלת Cast למשתמשים קיימים, כדי ליידע אותם שאפליקציית השולח תומכת עכשיו בהעברה (cast) ועוזרת למשתמשים חדשים ב-Google Cast.
ה-Cast SDK מספק תצוגה מותאמת אישית, IntroductoryOverlay
, שניתן להשתמש בה כדי להדגיש את לחצן הפעלת Cast כשהוא מוצג לראשונה למשתמשים. מוסיפים את הקוד הבא ל-VideoBrowserActivity
:
import com.google.android.gms.cast.framework.IntroductoryOverlay
import android.os.Looper
private var mIntroductoryOverlay: IntroductoryOverlay? = null
private fun showIntroductoryOverlay() {
mIntroductoryOverlay?.remove()
if (mediaRouteMenuItem?.isVisible == true) {
Looper.myLooper().run {
mIntroductoryOverlay = com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(
this@VideoBrowserActivity, mediaRouteMenuItem!!)
.setTitleText("Introducing Cast")
.setSingleTime()
.setOnOverlayDismissedListener(
object : IntroductoryOverlay.OnOverlayDismissedListener {
override fun onOverlayDismissed() {
mIntroductoryOverlay = null
}
})
.build()
mIntroductoryOverlay!!.show()
}
}
}
עכשיו צריך להוסיף CastStateListener
ולהפעיל את השיטה showIntroductoryOverlay
כשמכשיר CAST זמין. לשם כך, צריך לשנות את השיטה onCreate
ולבטל את השיטות onResume
ו-onPause
כך שיתאימו לאפשרויות הבאות:
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener
private var mCastStateListener: CastStateListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastStateListener = object : CastStateListener {
override fun onCastStateChanged(newState: Int) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay()
}
}
}
mCastContext = CastContext.getSharedInstance(this)
}
override fun onResume() {
super.onResume()
mCastContext?.addCastStateListener(mCastStateListener!!)
}
override fun onPause() {
super.onPause()
mCastContext?.removeCastStateListener(mCastStateListener!!)
}
מנקים את נתוני האפליקציה או מסירים אותה מהמכשיר. לאחר מכן, לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד ושכבת-העל שמשמשת כמבוא אמורה להופיע (ניקוי נתוני האפליקציה אם שכבת-העל לא מוצגת).
10. שלט רחוק מורחב
כדי לעשות את זה, ברשימת המשימות לעיצוב של Google Cast נדרשת אפליקציית שולח כדי לספק בקר מורחב למדיה שמועברת. המיני-בקר הוא גרסת מסך מלא של המיני-בקר.
Cast SDK מספק ווידג'ט לשלט הרחוק המורחב שנקרא ExpandedControllerActivity
. זוהי מחלקה מופשטת שצריך לבצע מחלקה משנית כדי להוסיף לחצן הפעלת Cast.
קודם כול, יוצרים קובץ משאבים חדש בתפריט שנקרא expanded_controller.xml
, כדי שהבקר המורחב יספק את לחצן הפעלת Cast:
<?xml version="1.0" encoding="utf-8"?>
<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>
יצירת חבילה חדשה expandedcontrols
בחבילה com.google.sample.cast.refplayer
. בשלב הבא, יוצרים קובץ חדש בשם ExpandedControlsActivity.kt
בחבילה של com.google.sample.cast.refplayer.expandedcontrols
.
package com.google.sample.cast.refplayer.expandedcontrols
import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory
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
}
}
עכשיו מצהירים על ה-ExpandedControlsActivity
ב-AndroidManifest.xml
שבתוך התג application
מעל OPTIONS_PROVIDER_CLASS_NAME
:
<application>
...
<activity
android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
</activity>
...
</application>
עורכים את CastOptionsProvider
ומשנים את NotificationOptions
ואת CastMediaOptions
כדי להגדיר את פעילות היעד לExpandedControlsActivity
:
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
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()
}
צריך לעדכן את השיטה LocalPlayerActivity
loadRemoteMedia
כדי להציג את ExpandedControlsActivity
כשהמדיה המרוחקת נטענת:
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
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(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
לוחצים על הלחצן הפעלה כדי להפעיל את האפליקציה בנייד ולהעביר סרטון. אמור להופיע הבקר המורחב. חוזרים לרשימת הסרטונים וכשלוחצים על המיני-בקר, הבקר המורחב ייטען שוב. עוברים אל מחוץ לאפליקציה כדי לראות את ההתראה. כדי לטעון את הבקר המורחב, יש ללחוץ על תמונת ההתראה.
11. הוספת תמיכה ב-Cast Connect
ספריית Cast Connect מאפשרת לאפליקציות שולח קיימות לתקשר עם אפליקציות Android TV באמצעות פרוטוקול Cast. התכונה Cast Connect מתבססת על תשתית ההעברה, ואפליקציית Android TV משמשת כמקלטת.
יחסי תלות
הערה: כדי להטמיע את Cast Connect, הערך play-services-cast-framework
צריך להיות 19.0.0
ומעלה.
LaunchOptions
כדי להפעיל את אפליקציית Android TV, שנקראת גם Android Receiver, עלינו להגדיר את הדגל setAndroidReceiverCompatible
כ-true באובייקט LaunchOptions
. אובייקט LaunchOptions
הזה מכתיב את אופן ההפעלה של המקבל ומועבר אל ה-CastOptions
שמוחזר על ידי המחלקה CastOptionsProvider
. הגדרת הדגל שהוזכר למעלה ל-false
תפעיל את מקלט האינטרנט עבור מזהה האפליקציה שהוגדר ב-Cast Developer Console.
בקובץ CastOptionsProvider.kt
, מוסיפים את הקוד הבא ל-method getCastOptions
:
import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
.setAndroidReceiverCompatible(true)
.build()
return new CastOptions.Builder()
.setLaunchOptions(launchOptions)
...
.build()
הגדרה של פרטי כניסה להשקה
בצד השולח, תוכלו לציין CredentialsData
שמייצג את ההצטרפות לסשן. הערך credentials
הוא מחרוזת שניתן להגדיר על ידי המשתמש, כל עוד אפליקציית ה-ATV יכולה להבין אותה. CredentialsData
מועבר לאפליקציית Android TV רק בזמן ההשקה או ההצטרפות. אם מגדירים אותה שוב בזמן החיבור לאינטרנט, היא לא תועבר לאפליקציית Android TV.
כדי להגדיר פרטי כניסה להשקה, צריך להגדיר את CredentialsData
ולהעביר אותו לאובייקט LaunchOptions
. צריך להוסיף את הקוד הבא לשיטת getCastOptions
בקובץ CastOptionsProvider.kt
:
import com.google.android.gms.cast.CredentialsData
...
val credentialsData = CredentialsData.Builder()
.setCredentials("{\"userId\": \"abc\"}")
.build()
val launchOptions = LaunchOptions.Builder()
...
.setCredentialsData(credentialsData)
.build()
הגדרת פרטי כניסה ב-LoadRequest
אם אפליקציית Web Receiver ואפליקציית Android TV מטפלים ב-credentials
באופן שונה, יכול להיות שיהיה צורך להגדיר credentials
נפרד לכל אחד מהם. כדי לפתור את הבעיה, מוסיפים את הקוד הבא בקובץ LocalPlayerActivity.kt
במסגרת הפונקציה loadRemoteMedia
:
remoteMediaClient.load(MediaLoadRequestData.Builder()
...
.setCredentials("user-credentials")
.setAtvCredentials("atv-user-credentials")
.build())
בהתאם לאפליקציית הנמען שאליה השולח מעביר, ערכת ה-SDK תטפל עכשיו באופן אוטומטי בפרטי הכניסה שבהם יש להשתמש בסשן הנוכחי.
בדיקה של Cast Connect
שלבים להתקנת ה-APK של Android TV ב-Chromecast with Google TV
- מאתרים את כתובת ה-IP של מכשיר ה-Android TV. בדרך כלל, מגדירים את האפשרות הזו בקטע הגדרות > רשת ואינטרנט > (שם הרשת שאליה המכשיר מחובר). בצד שמאל יוצגו הפרטים וכתובת ה-IP של המכשיר ברשת.
- צריך להשתמש בכתובת ה-IP של המכשיר כדי להתחבר אליה דרך ADB באמצעות הטרמינל:
$ adb connect <device_ip_address>:5555
- מחלון ה-Terminal, עוברים לתיקייה ברמה העליונה של דוגמאות Codelab שהורדתם בתחילת ה-Codelab הזה. לדוגמה:
$ cd Desktop/android_codelab_src
- מתקינים את קובץ ה- .apk בתיקייה הזו ב-Android TV על ידי הפעלת:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- עכשיו אמורה להופיע אפליקציה בשם העברת סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
- חוזרים לפרויקט Android Studio ולוחצים על הלחצן 'הפעלה' כדי להתקין ולהפעיל את אפליקציית השולח במכשיר הנייד הפיזי. בפינה השמאלית העליונה, לוחצים על סמל ההעברה ובוחרים את מכשיר Android TV מבין האפשרויות הזמינות. עכשיו אתם אמורים לראות שאפליקציית Android TV מופעלת במכשיר Android TV שלכם, והפעלה של סרטון אמורה לאפשר לכם לשלוט בהפעלת הסרטון באמצעות השלט הרחוק של Android TV.
12. התאמה אישית של ווידג'טים של Cast
ניתן להתאים אישית ווידג'טים בהעברה (cast) על ידי הגדרת הצבעים, עיצוב הלחצנים, הטקסט ומראה התמונה הממוזערת ועל ידי בחירת סוגי הלחצנים להצגה.
עדכון res/values/styles_castvideo.xml
<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
<item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
<item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
<item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
<item name="castExpandedControllerToolbarStyle">
@style/ThemeOverlay.AppCompat.ActionBar
</item>
...
</style>
יש להצהיר על העיצובים המותאמים אישית הבאים:
<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
<item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
<item name="mediaRouteButtonTint">#EEFF41</item>
</style>
<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
<item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
<item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
<item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
<item name="android:textColor">#FFFFFF</item>
</style>
<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
<item name="castShowImageThumbnail">true</item>
<item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
<item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
<item name="castBackground">@color/accent</item>
<item name="castProgressBarColor">@color/orange</item>
</style>
<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
<item name="castButtonColor">#FFFFFF</item>
<item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
<item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
<item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>
13. מזל טוב
עכשיו אתה יודע איך להפעיל העברה (Cast) של אפליקציית וידאו באמצעות הווידג'טים של Cast SDK ב-Android.
לפרטים נוספים, עיין במדריך למפתחים של שולח Android.