1. סקירה כללית
בשיעור הזה תלמדו איך לשנות אפליקציית וידאו קיימת ב-Android כדי להפעיל Cast של תוכן במכשיר שתומך ב-Google Cast.
מה זה Google Cast?
מערכת Google Cast מאפשרת למשתמשים להעביר תוכן מטלפון נייד לטלוויזיה. המשתמשים יוכלו להשתמש בנייד שלהם כשלט רחוק להפעלת מדיה בטלוויזיה.
Google Cast SDK מאפשר לכם להרחיב את האפליקציה כדי לשלוט בטלוויזיה או במערכת סאונד. ה-SDK של Cast מאפשר לך להוסיף את רכיבי ממשק המשתמש הנחוצים על סמך רשימת המשימות לעיצוב של Google Cast.
רשימת המשימות לעיצוב של Google Cast מסופקת כדי להפוך את חוויית המשתמש של Cast לפשוטה וצפויה בכל הפלטפורמות הנתמכות.
מה אנחנו מתכוונים לבנות?
בסיום ה-Codelab הזה, תהיה לך אפליקציית וידאו ל-Android, שיכולה להעביר סרטונים למכשיר שתומך ב-Google Cast.
מה תלמדו
- איך להוסיף את Google Cast SDK לאפליקציית סרטונים לדוגמה.
- איך להוסיף את לחצן הפעלת Cast כדי לבחור מכשיר 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 > (קובץ >) חדש > אפשרויות התפריט 'ייבוא פרויקט...'.
בוחרים את הספרייה 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
מתוך הורדת הקוד לדוגמה (בוחרים באפשרות ייבוא פרויקט במסך הפתיחה או באפשרות בתפריט קובץ > חדש > ייבוא פרויקט...). - לוחצים על הלחצן 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 הזה. בכל שלב במהלך הפעלת 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, צריך להירשם כמפתח 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
באופן דומה.
לחצו על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. הלחצן להפעלת Cast אמור להופיע בסרגל הפעולות של האפליקציה, וכשתלחצו עליו הוא יציג את רשימת מכשירי ה-Cast ברשת המקומית. גילוי המכשיר מנוהל באופן אוטומטי על ידי CastContext
. עליך לבחור את מכשיר ה-Cast שלך ואפליקציית המקלט לדוגמה תיטען במכשיר ה-Cast. ניתן לנווט בין פעילות הגלישה לבין הפעילות המקומית בנגן, ומצב הלחצן להפעלת Cast נשמר באופן מסונכרן.
עדיין לא ביקשנו תמיכה בהפעלת מדיה, ולכן עדיין אי אפשר להפעיל סרטונים במכשיר Cast. אפשר ללחוץ על הלחצן להפעלת Cast כדי להתנתק.
6. העברה (cast) של תוכן וידאו
נרחיב את האפליקציה לדוגמה כדי להפעיל סרטונים מרחוק במכשיר Cast. כדי לעשות זאת, עלינו להאזין לאירועים השונים שנוצרו על ידי ה-Cast framework.
הפעלת Cast של מדיה
ככלל, אם רוצים להפעיל מדיה במכשיר Cast, צריך לבצע את הפעולות הבאות:
- יוצרים אובייקט
MediaInfo
שמשמש ליצירת מודל של פריט מדיה. - צריך להתחבר למכשיר Cast ולהפעיל את אפליקציית המקבל.
- טוענים את האובייקט
MediaInfo
במקלט ומפעילים את התוכן. - עוקבים אחרי סטטוס המדיה.
- שליחת פקודות הפעלה למקלט על סמך אינטראקציות של המשתמש.
כבר ביצענו את שלב 2 בקטע הקודם. קל לבצע את שלב 3 עם Cast framework. שלב 1 מסתכם במיפוי של אובייקט אחד לאובייקט אחר; MediaInfo
הוא משהו ש-Cast framework מבינה, ו-MediaItem
הוא הסתרת פריט מדיה באפליקציה שלנו; אנחנו יכולים למפות בקלות MediaItem
לMediaInfo
.
האפליקציה לדוגמה LocalPlayerActivity
כבר יוצרת הבחנה בין הפעלה מקומית לבין הפעלה מרחוק באמצעות enum הבא:
private var mLocation: PlaybackLocation? = null
enum class PlaybackLocation {
LOCAL, REMOTE
}
enum class PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE
}
ב-Codelab הזה לא חשוב שתבינו בדיוק איך פועלת כל הלוגיקה של הנגן לדוגמה. חשוב להבין שיהיה צורך לשנות את נגן המדיה של האפליקציה כדי להיות מודעים לשני מיקומי ההפעלה באופן דומה.
הנגן המקומי תמיד נמצא במצב הפעלה מקומי, כי הוא עדיין לא יודע דבר על מצבי ההעברה. אנחנו צריכים לעדכן את ממשק המשתמש על סמך מעברי מצבים שמתרחשים ב-Cast framework. לדוגמה, אם אנחנו מתחילים להפעיל Cast, צריך להפסיק את ההפעלה המקומית ולהשבית חלק מהפקדים. באופן דומה, אם נפסיק את ההעברה במהלך פעילות זו, נצטרך לעבור להפעלה מקומית. כדי לטפל בכך, עלינו להאזין לאירועים השונים שנוצרו על ידי Cast framework.
ניהול הפעלת 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 ולאתחל כמה משתנים שנשתמש בהם בפעילות. שינוי השיטה onCreate
LocalPlayerActivity
ל:
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. אפשר לגשת ל-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()
}
ב-method updatePlayButton
, משנים את הערך של המשתנה isConnected
:
private fun updatePlayButton(state: PlaybackState?) {
...
val isConnected = (mCastSession != null
&& (mCastSession!!.isConnected || mCastSession!!.isConnecting))
...
}
עכשיו לחצו על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. מחברים את מכשיר Cast ומתחילים להפעיל סרטון. הסרטון אמור לפעול במקלט.
7. מיני-בקר
לפי רשימת המשימות של עיצוב Cast, כל אפליקציית 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 כדי להפעיל את האפליקציה ולהעביר (cast) סרטון. כשההפעלה תתחיל במקלט, המיני-בקר יופיע בחלק התחתון של כל פעילות. אפשר לשלוט בהפעלה מרחוק באמצעות המיני-בקר. אם מנווטים בין פעילות העיון לפעילות הנגן המקומית, מצב המיני של הבקר אמור להישאר מסונכרן עם סטטוס הפעלת המדיה במקלט.
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()
}
לחצו על הלחצן הפעלה כדי להפעיל את האפליקציה במכשיר הנייד. אפשר להפעיל Cast של סרטון ולנווט אל מחוץ לאפליקציה לדוגמה. אמורה להיות התראה לגבי הסרטון שמופעל עכשיו במקלט. צריך לנעול את המכשיר הנייד, ומסך הנעילה אמור להציג פקדים להפעלת המדיה במכשיר ה-Cast.
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
וקוראים ל-method 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()
}
צריך לעדכן את ה-method 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 היעדים, צריך להגדיר את הדגל 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.
כדי להגדיר את פרטי הכניסה להפעלה, צריך להגדיר ולהעביר את CredentialsData
לאובייקט LaunchOptions
. צריך להוסיף את הקוד הבא ל-method 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())
בהתאם לאפליקציית המקבל שאליה השולח מעביר את התוכן, עכשיו ה-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 ולוחצים על לחצן ההפעלה כדי להתקין את & להריץ את אפליקציית השולח במכשיר הנייד הפיזי שלך. בפינה השמאלית העליונה, לוחצים על הסמל להפעלת 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.