1. סקירה כללית
בקודלאב הזה תלמדו איך לשנות אפליקציית וידאו קיימת ל-Android כדי להעביר תוכן למכשיר שתומך ב-Google Cast.
מה זה Google Cast?
Google Cast מאפשר למשתמשים להעביר תוכן מהנייד לטלוויזיה. לאחר מכן, המשתמשים יכולים להשתמש בנייד שלהם כשלט רחוק להפעלת מדיה בטלוויזיה.
Google Cast SDK מאפשר לכם להרחיב את האפליקציה כדי לשלוט בטלוויזיה או במערכת סאונד. ה-SDK של Cast מאפשר לך להוסיף את רכיבי ממשק המשתמש הנחוצים על סמך רשימת המשימות של Google Cast Design.
רשימת המשימות לעיצוב של Google Cast נועדה להפוך את חוויית המשתמש ב-Cast לפשוטה ולצפויה בכל הפלטפורמות הנתמכות.
מה אנחנו מתכוונים לבנות?
בסיום ה-Codelab הזה, תהיה לך אפליקציית וידאו ל-Android, שדרכה אפשר להעביר סרטונים למכשיר שתומך ב-Google Cast.
מה תלמדו
- איך מוסיפים את Google Cast SDK לאפליקציית וידאו לדוגמה.
- איך מוסיפים את לחצן ההעברה כדי לבחור מכשיר Google Cast.
- איך מתחברים למכשיר 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.
- כדי לבדוק את השילוב של Cast Connect צריך מכשיר Chromecast with Google TV, אבל הוא אופציונלי בשאר הקורס Codelab. אם אין לכם מכשיר כזה, אתם יכולים לדלג על השלב הוספת תמיכה ב-Cast Connect לקראת סוף המדריך הזה.
ניסיון
- נדרש ידע קודם בפיתוח ב-Kotlin וב-Android.
- בנוסף, נדרשת לך ניסיון קודם בצפייה בטלוויזיה :)
איך תוכלו להשתמש במדריך הזה?
איזה דירוג מגיע לחוויה שלך עם בניית אפליקציות ל-Android?
איזה דירוג מגיע לדעתך לחוויה שלך עם הצפייה בטלוויזיה?
2. קבלת קוד לדוגמה
אפשר להוריד את כל הקוד לדוגמה למחשב...
ופורקים את קובץ ה-ZIP שהורדתם.
3. הרצת האפליקציה לדוגמה
קודם נראה איך נראית אפליקציית הדוגמה המושלמת. האפליקציה היא נגן וידאו בסיסי. המשתמש יכול לבחור סרטון מתוך רשימה, ואז להפעיל אותו באופן מקומי במכשיר או להעביר אותו (cast) למכשיר Google Cast.
אחרי שהורדתם את הקוד, ההוראות הבאות מתארות איך לפתוח ולהפעיל את האפליקציה לדוגמה שהושלמה ב-Android Studio:
בוחרים באפשרות Import Project (ייבוא פרויקט) במסך הפתיחה או באפשרויות התפריט File > New > Import Project… (קובץ > חדש > ייבוא פרויקט…).
בוחרים את הספרייה app-done
מתיקיית הקוד לדוגמה ולוחצים על 'אישור'.
לוחצים על קובץ > סנכרון הפרויקט עם קובצי Gradle
מפעילים ניפוי באגים ב-USB במכשיר Android – ב-Android מגרסה 4.2 ואילך, מסך האפשרויות למפתחים מוסתר כברירת מחדל. כדי להציג אותו, עוברים אל הגדרות > מידע על הטלפון ומקישים על מספר Build שבע פעמים. חוזרים למסך הקודם, עוברים אל מערכת > מתקדם ומקישים על אפשרויות למפתחים בתחתית המסך, ואז מקישים על ניפוי באגים ב-USB כדי להפעיל אותה.
מחברים את מכשיר Android ולוחצים על הלחצן הפעלה ב-Android Studio. אפליקציית הווידאו בשם הפעלת Cast של סרטונים אמורה להופיע לאחר מספר שניות.
לוחצים על הלחצן להפעלת Cast באפליקציית הווידאו ובוחרים את מכשיר ה-Google Cast.
בוחרים סרטון ולוחצים על לחצן ההפעלה.
הסרטון יתחיל לפעול במכשיר Google Cast.
הבקר המורחב יוצג. אפשר להשתמש בלחצן ההפעלה וההשהיה כדי לשלוט בהפעלה.
חוזרים לרשימת הסרטונים.
מיני-בקר מוצג עכשיו בתחתית המסך.
אפשר ללחוץ על לחצן ההשהיה במיני-בקר כדי להשהות את הסרטון במקלט. לוחצים על לחצן ההפעלה בנגן המיני כדי להמשיך את הפעלת הסרטון.
לוחצים על לחצן דף הבית של המכשיר הנייד. מושכים למטה את ההתראות ועכשיו אמורה להופיע התראה לגבי סשן ההעברה (cast).
תוכלו לנעול את הטלפון, וכשמבטלים את הנעילה שלו אמורה להופיע התראה במסך הנעילה, עם הרשאה לשלוט בהפעלת המדיה או להפסיק את הפעלת ה-Cast.
חוזרים לאפליקציית הווידאו ולוחצים על לחצן הפעלת Cast כדי להפסיק את ההעברה למכשיר Google Cast.
שאלות נפוצות
4. מכינים את הפרויקט לתחילת העבודה
אנחנו צריכים להוסיף תמיכה ב-Google Cast לאפליקציית ההתחלה שהורדתם. הנה כמה מונחים של Google Cast שנשתמש בהם ב-Codelab הזה:
- אפליקציית שליחה שפועלת במכשיר נייד או במחשב נייד,
- אפליקציית מקלט פועלת במכשיר Google Cast.
עכשיו אתם מוכנים לפתח את הפרויקט באמצעות Android Studio:
- בוחרים את הספרייה
app-start
מההורדה של קוד לדוגמה (בוחרים באפשרות Import Project במסך הפתיחה או באפשרות File > New > Import Project... בתפריט). - לוחצים על הלחצן
Sync Project with Gradle Files.
- לוחצים על הלחצן
Run כדי להפעיל את האפליקציה ולחקור את ממשק המשתמש.
עיצוב אפליקציות
האפליקציה מאחזרת רשימה של סרטונים משרת אינטרנט מרוחק ומספקת רשימה של סרטונים שהמשתמשים יכולים לעיין בהם. המשתמשים יכולים לבחור סרטון כדי לראות את הפרטים שלו או להפעיל אותו באופן מקומי בנייד.
האפליקציה מורכבת משתי פעילויות עיקריות: 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 או להתנתק ממנו בכל פעילות באפליקציה, כפי שמתואר ברשימת המשימות לעיצוב של 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 יש אובייקט גלובלי מסוג סינגלטון, CastContext
, שמרכז את כל האינטראקציות של Cast.
עליכם להטמיע את הממשק 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
ל-framework של Cast:
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
באופן דומה.
לוחצים על הלחצן Run כדי להריץ את האפליקציה בנייד. לחצן Cast אמור להופיע בסרגל הפעולות של האפליקציה. כשלוחצים עליו, מוצגת רשימה של מכשירי Cast ברשת המקומית. זיהוי המכשירים מנוהל באופן אוטומטי על ידי
CastContext
. עליך לבחור את מכשיר ה-Cast שלך ואפליקציית המקלט לדוגמה תיטען במכשיר ה-Cast. ניתן לנווט בין פעילות הגלישה לבין הפעילות המקומית בנגן, ומצב הלחצן להפעלת Cast נשמר בסנכרון.
עדיין לא הוספנו תמיכה בהפעלת מדיה, ולכן אי אפשר להפעיל סרטונים במכשיר ההעברה (cast). אפשר ללחוץ על הלחצן להפעלת Cast כדי להתנתק.
6. העברה (cast) של תוכן וידאו
נרחיב את האפליקציה לדוגמה כך שתאפשר גם להפעיל סרטונים מרחוק במכשיר Cast. כדי לעשות זאת, עלינו להאזין לאירועים השונים שנוצרו על ידי Cast frame
העברת מדיה
באופן כללי, כדי להפעיל מדיה במכשיר Cast, צריך לבצע את הפעולות הבאות:
- יוצרים אובייקט
MediaInfo
שמתאר פריט מדיה. - צריך להתחבר למכשיר Cast ולהפעיל את אפליקציית המקבל.
- טוענים את האובייקט
MediaInfo
למכשיר המקבל ומפעילים את התוכן. - עוקבים אחרי סטטוס המדיה.
- שליחת פקודות הפעלה למקלט על סמך אינטראקציות של המשתמש.
כבר ביצענו את שלב 2 בקטע הקודם. קל לבצע את שלב 3 באמצעות מסגרת Cast. שלב 1 הוא למעשה מיפוי של אובייקט אחד לאובייקט אחר. MediaInfo
הוא אובייקט שמערכת Cast מבינה, ו-MediaItem
הוא האנקפסולציה של פריט המדיה באפליקציה שלנו. אנחנו יכולים למפות בקלות MediaItem
ל-MediaInfo
.
באפליקציית הדוגמה LocalPlayerActivity
כבר יש הבחנה בין הפעלה מקומית לבין הפעלה מרחוק באמצעות המאפיין enum הזה:
private var mLocation: PlaybackLocation? = null
enum class PlaybackLocation {
LOCAL, REMOTE
}
enum class PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE
}
לא חשוב להבין בדיוק איך כל הלוגיקה של הנגן לדוגמה פועלת במסגרת הקוד הפתוח הזה. חשוב להבין שיהיה צורך לשנות את נגן המדיה של האפליקציה כדי שיהיה מודע לשני מיקומי ההפעלה באופן דומה.
בשלב הזה הנגן המקומי תמיד נמצא במצב ההפעלה המקומי כי הוא עדיין לא יודע על המצבים של ההעברה (cast). אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי המצבים שמתרחשים במסגרת Cast. לדוגמה, אם מתחילים העברה (cast), צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהפקדים. באופן דומה, אם נפסיק את ההעברה (cast) כשאנחנו בפעילות הזו, נצטרך לעבור להפעלה מקומית. כדי לעשות זאת, אנחנו צריכים להאזין לאירועים השונים שנוצרים על ידי מסגרת 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()
. סשנים נוצרים ומסתיימים באופן אוטומטי בתגובה לאינטראקציות של המשתמשים עם תיבות הדו-שיח של Cast.
אנחנו צריכים לרשום את ה-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
שתומך בהפעלת מדיה, ה-SDK ייצור באופן אוטומטי מופע של RemoteMediaClient
. אפשר לגשת ל-method 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"/>
לוחצים על הלחצן Run כדי להריץ את האפליקציה ולהעביר סרטון. כשההפעלה תתחיל במקלט, המיני-בקר יופיע בחלק התחתון של כל פעילות. אפשר לשלוט בהפעלה מרחוק באמצעות השלט הרחוק המיניאטורי. אם מנווטים בין פעילות העיון לפעילות הנגן המקומית, מצב המיני של הבקר אמור להישאר מסונכרן עם סטטוס הפעלת המדיה במקלט.
8. התראה ומסך הנעילה
כדי להשתמש ברשימת המשימות לעיצוב של Google Cast, צריך שאפליקציית השולח תטמיע פקדי מדיה מהתראה וממסך הנעילה.
ה-Cast SDK מספק MediaNotificationService
כדי לעזור לאפליקציית השולח ליצור פקדי מדיה להתראות ולמסך הנעילה. השירות ממוזג באופן אוטומטי עם המניפסט של האפליקציה באמצעות GRid.
ה-MediaNotificationService
יפעל ברקע כששולח מבצע העברה (cast), ותוצג התראה עם תמונה ממוזערת של התמונה ומטא-נתונים לגבי פריט ההעברה הנוכחי, לחצן הפעלה/השהיה ולחצן עצירה.
אפשר להפעיל את פקדי ההתראות ומסך הנעילה עם 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()
}
לוחצים על הלחצן Run כדי להריץ את האפליקציה בנייד. מעבירים סרטון ומנווטים מחוץ לאפליקציה לדוגמה. אמורה להופיע התראה על הסרטון שפועל כרגע במכשיר המקבל. נועלים את המכשיר הנייד, ומסך הנעילה אמור להציג עכשיו את הפקדים להפעלת המדיה במכשיר ההעברה.
9. שכבת-על של מבצע היכרות
כדי להשתמש ברשימת המשימות לעיצוב של Google 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!!)
}
מנקים את נתוני האפליקציה או מסירים אותה מהמכשיר. לאחר מכן, לוחצים על הלחצן Run (הפעלה) כדי להפעיל את האפליקציה במכשיר הנייד. אמורה להופיע שכבת-על עם הסבר על האפליקציה (אם שכבת-העל לא מוצגת, צריך לנקות את נתוני האפליקציה).
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 מבוסס על התשתית של Cast, והאפליקציה של Android TV משמשת כמקלט.
יחסי תלות
הערה: כדי להטמיע את Cast Connect, הערך של play-services-cast-framework
צריך להיות 19.0.0
ומעלה.
LaunchOptions
כדי להפעיל את אפליקציית Android TV, שנקראת גם Android Receiver, צריך להגדיר את הדגל setAndroidReceiverCompatible
לערך true באובייקט LaunchOptions
. אובייקט LaunchOptions
הזה קובע את אופן ההפעלה של המקבל ומועבר אל CastOptions
שמוחזרת על ידי המחלקה CastOptionsProvider
. הגדרת הסימון שלמעלה כ-false
תפעיל את מקלט האינטרנט עבור מזהה האפליקציה המוגדר ב-Cast 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.
כדי להגדיר את Launch Credentials, צריך להגדיר את 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
אם אפליקציית מקבל האינטרנט והאפליקציה ל-Android TV מטפלים בcredentials
באופן שונה, יכול להיות שיהיה צורך להגדיר credentials
נפרד לכל אחד מהם. כדי לטפל בבעיה הזו, מוסיפים את הקוד הבא לקובץ LocalPlayerActivity.kt
, בפונקציה loadRemoteMedia
:
remoteMediaClient.load(MediaLoadRequestData.Builder()
...
.setCredentials("user-credentials")
.setAtvCredentials("atv-user-credentials")
.build())
בהתאם לאפליקציית המקבל שאליה השולח מבצע העברה (cast), ה-SDK יטפל עכשיו באופן אוטומטי בפרטי הכניסה שבהם יש להשתמש בסשן הנוכחי.
בדיקה של Cast Connect
שלבים להתקנת ה-APK של Android TV ב-Chromecast with Google TV
- מוצאים את כתובת ה-IP של מכשיר Android TV. בדרך כלל, המדיניות זמינה בקטע הגדרות > רשת ו אינטרנט > (שם הרשת שאליה המכשיר מחובר). בצד שמאל יוצגו הפרטים ואת כתובת ה-IP של המכשיר שלך ברשת.
- משתמשים בכתובת ה-IP של המכשיר כדי להתחבר אליו דרך ADB באמצעות הטרמינל:
$ adb connect <device_ip_address>:5555
- מחלון הטרמינל, עוברים לתיקייה ברמה העליונה של דוגמאות ה-Codelab שהורדתם בתחילת השיעור הזה ב-Codelab. לדוגמה:
$ cd Desktop/android_codelab_src
- כדי להתקין את קובץ ה-APK בתיקייה הזו ב-Android TV, מריצים את הפקודה:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- עכשיו אמורה להופיע אפליקציה בשם הפעלת Cast של סרטונים בתפריט האפליקציות שלך במכשיר Android TV.
- חוזרים לפרויקט ב-Android Studio ולוחצים על הלחצן Run כדי להתקין ולהפעיל את אפליקציית השליחה בנייד הפיזי. בפינה השמאלית העליונה, לוחצים על הסמל להפעלת Cast ובוחרים את מכשיר 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 Sender.