ערוצים במסך הבית

מסך הבית של Android TV מספק ממשק משתמש שמציג תוכן מומלץ כטבלה של ערוצים ותוכניות. כל שורה מייצגת ערוץ. ערוץ מכיל כרטיסים לכל תוכנית שזמינה בערוץ הזה:

מסך הבית של Android TV
מסך הבית של Android TV

במסמך הזה מוסבר איך להוסיף ערוצים ותוכניות למסך הבית, לעדכן תוכן, לטפל בפעולות של משתמשים ולספק את חוויית המשתמש הטובה ביותר. (אם אתם רוצים להתעמק ב-API, נסו את שיעור ה-Codelab של מסך הבית וצפו בסשן של Android TV ב-I/O 2017)

ממשק המשתמש של מסך הבית

אפליקציות יכולות ליצור ערוצים חדשים, להוסיף, להסיר ולעדכן את התוכניות בערוץ, ולשלוט בסדר התוכניות בערוץ. לדוגמה, אפליקציה יכולה ליצור ערוץ בשם "מה חדש" ולהציג כרטיסים של תוכניות חדשות שזמינות.

האפליקציות לא יכולות לשלוט בסדר שבו הערוצים מופיעים במסך הבית. כשהאפליקציה יוצרת ערוץ חדש, מסך הבית מוסיף אותו לתחתית רשימת הערוצים. המשתמש יכול לשנות את הסדר של הערוצים, להסתיר אותם ולהציג אותם.

הערוץ "הצעות לצפייה"

הערוץ "הצעות לצפייה" הוא השורה השנייה שמופיעה במסך הבית, אחרי שורת האפליקציות. המערכת יוצרת את הערוץ הזה ומתחזקת אותו. האפליקציה יכולה להוסיף תוכניות לערוץ "הצעות לצפייה". מידע נוסף זמין במאמר הוספת תוכניות לערוץ "הצעות לצפייה".

ערוצי אפליקציות

כל הערוצים שהאפליקציה יוצרת פועלים לפי מחזור החיים הבא:

  1. המשתמש מגלה ערוץ באפליקציה ומבקש להוסיף אותו למסך הבית.
  2. האפליקציה יוצרת את הערוץ ומוסיפה אותו אל TvProvider (בשלב הזה הערוץ לא גלוי).
  3. האפליקציה מבקשת מהמערכת להציג את הערוץ.
  4. המערכת מבקשת מהמשתמש לאשר את הערוץ החדש.
  5. הערוץ החדש מופיע בשורה האחרונה של מסך הבית.

ערוץ ברירת המחדל

האפליקציה יכולה להציע למשתמש להוסיף למסך הבית כמה ערוצים שהוא רוצה. בדרך כלל המשתמש צריך לבחור ולאשר כל ערוץ לפני שהוא מופיע במסך הבית. בכל אפליקציה יש אפשרות ליצור ערוץ ברירת מחדל אחד. ערוץ ברירת המחדל הוא מיוחד כי הוא מופיע באופן אוטומטי במסך הבית, והמשתמש לא צריך לבקש אותו באופן מפורש.

דרישות מוקדמות

מסך הבית של Android TV משתמש בממשקי ה-API של TvProvider ב-Android כדי לנהל את הערוצים והתוכניות שהאפליקציה יוצרת. כדי לגשת לנתונים של הספק, צריך להוסיף את ההרשאה הבאה למניפסט של האפליקציה:

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />

ספריית התמיכה של TvProvider מקלה על השימוש בספק. מוסיפים אותו ליחסי התלות בקובץ build.gradle:

מגניב

implementation 'androidx.tvprovider:tvprovider:1.0.0'

Kotlin

implementation("androidx.tvprovider:tvprovider:1.0.0")

כדי לעבוד עם ערוצים ותוכניות, חשוב לכלול את הייבוא של ספריית התמיכה הזו בתוכנית:

Kotlin

import android.support.media.tv.Channel
import android.support.media.tv.TvContractCompat
import android.support.media.tv.ChannelLogoUtils
import android.support.media.tv.PreviewProgram
import android.support.media.tv.WatchNextProgram

Java

import android.support.media.tv.Channel;
import android.support.media.tv.TvContractCompat;
import android.support.media.tv.ChannelLogoUtils;
import android.support.media.tv.PreviewProgram;
import android.support.media.tv.WatchNextProgram;

ערוצים

הערוץ הראשון שהאפליקציה יוצרת הופך לערוץ ברירת המחדל שלה. הערוץ שמוגדר כברירת מחדל מופיע באופן אוטומטי במסך הבית. המשתמש צריך לבחור ולאשר את כל הערוצים האחרים שאתם יוצרים כדי שהם יופיעו במסך הבית.

יצירת ערוץ

האפליקציה צריכה לבקש מהמערכת להציג ערוצים חדשים שנוספו רק כשהיא פועלת בחזית. כך האפליקציה לא תציג תיבת דו-שיח שמבקשת אישור להוסיף את הערוץ בזמן שהמשתמש מפעיל אפליקציה אחרת. אם תנסו להוסיף ערוץ בזמן שהאפליקציה פועלת ברקע, השיטה onActivityResult() של הפעילות תחזיר את קוד הסטטוס RESULT_CANCELED.

כדי ליצור ערוץ, פועלים לפי השלבים הבאים:

  1. יוצרים בונה ערוצים ומגדירים את המאפיינים שלו. שימו לב שסוג הערוץ צריך להיות TYPE_PREVIEW. מוסיפים עוד מאפיינים לפי הצורך.

    Kotlin

    val builder = Channel.Builder()
    // Every channel you create must have the type `TYPE_PREVIEW`
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri)
    

    Java

    Channel.Builder builder = new Channel.Builder();
    // Every channel you create must have the type `TYPE_PREVIEW`
    builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
            .setDisplayName("Channel Name")
            .setAppLinkIntentUri(uri);
    
  2. מוסיפים את הערוץ לספק:

    Kotlin

    var channelUri = context.contentResolver.insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues())
    

    Java

    Uri channelUri = context.getContentResolver().insert(
            TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues());
    
  3. צריך לשמור את מזהה הערוץ כדי להוסיף תוכניות לערוץ מאוחר יותר. מחלצים את מזהה הערוץ מה-URI שהוחזר:

    Kotlin

    var channelId = ContentUris.parseId(channelUri)
    

    Java

    long channelId = ContentUris.parseId(channelUri);
    
  4. אתם צריכים להוסיף לוגו לערוץ. משתמשים ב-Uri או ב-Bitmap. סמל הלוגו צריך להיות בגודל 80dp x 80dp, והוא חייב להיות אטום. הוא מוצג מתחת למסכה עגולה:

    מסכת סמל של מסך הבית בטלוויזיה

    Kotlin

    // Choose one or the other
    storeChannelLogo(context: Context, channelId: Long, logoUri: Uri) // also works if logoUri is a URL
    storeChannelLogo(context: Context, channelId: Long, logo: Bitmap)
    

    Java

    // Choose one or the other
    storeChannelLogo(Context context, long channelId, Uri logoUri); // also works if logoUri is a URL
    storeChannelLogo(Context context, long channelId, Bitmap logo);
    
  5. יצירת הערוץ שמוגדר כברירת מחדל (אופציונלי): כשהאפליקציה יוצרת את הערוץ הראשון שלה, אתם יכולים להגדיר אותו כערוץ שמוגדר כברירת מחדל כדי שהוא יופיע במסך הבית באופן מיידי בלי שום פעולה מצד המשתמש. ערוצים אחרים שתיצרו לא יהיו גלויים עד שהמשתמש יבחר בהם באופן מפורש.

    Kotlin

    TvContractCompat.requestChannelBrowsable(context, channelId)
    

    Java

    TvContractCompat.requestChannelBrowsable(context, channelId);
    
  6. הגדירו שערוץ ברירת המחדל יופיע לפני שהאפליקציה נפתחת. כדי שזה יקרה, מוסיפים BroadcastReceiver שמאזין לפעולה android.media.tv.action.INITIALIZE_PROGRAMS, שמסך הבית שולח אחרי שהאפליקציה מותקנת:

    <receiver
      android:name=".RunOnInstallReceiver"
      android:exported="true">
        <intent-filter>
          <action android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>
    

    כשאתם מתקינים את האפליקציה ממקור לא ידוע במהלך הפיתוח, אתם יכולים לבדוק את השלב הזה על ידי הפעלת ה-Intent דרך adb, כאשר your.package.name/.YourReceiverName הוא ה-BroadcastReceiver של האפליקציה:

    adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \
        your.package.name/.YourReceiverName
    

    במקרים נדירים, האפליקציה עשויה לקבל את השידור באותו זמן שהמשתמש מפעיל את האפליקציה. ודאו שהקוד לא מנסה להוסיף את הערוץ שמוגדר כברירת מחדל יותר מפעם אחת.

עדכון ערוץ

עדכון ערוצים דומה מאוד ליצירת ערוצים.

משתמשים בChannel.Builder אחר כדי להגדיר את המאפיינים שצריך לשנות.

משתמשים ב-ContentResolver כדי לעדכן את הערוץ. משתמשים במזהה הערוץ ששמרתם כשהערוץ נוסף במקור:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildChannelUri(channelId),
        builder.build().toContentValues(),
        null,
        null
)

Java

context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId),
    builder.build().toContentValues(), null, null);

כדי לעדכן את הלוגו של הערוץ, משתמשים ב-storeChannelLogo().

מחיקת ערוץ

Kotlin

context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);

תוכניות

תוכניות הן כרטיסי התוכן הנפרדים שמוצגים בערוץ. אתם יכולים לפרסם תוכניות גם בערוצים המותאמים אישית של האפליקציה וגם בערוץ "הצעות לצפייה" שמנוהל על ידי המערכת.

הוספת תוכניות לערוץ של אפליקציה

יוצרים PreviewProgram.Builder ומגדירים את המאפיינים שלו:

Kotlin

val builder = PreviewProgram.Builder()
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId)

Java

PreviewProgram.Builder builder = new PreviewProgram.Builder();
builder.setChannelId(channelId)
        .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
        .setTitle("Title")
        .setDescription("Program description")
        .setPosterArtUri(uri)
        .setIntentUri(uri)
        .setInternalProviderId(appProgramId);

מוסיפים עוד מאפיינים בהתאם לסוג התוכנית. (כדי לראות את המאפיינים שזמינים לכל סוג של תוכנית, עיינו בטבלאות שבהמשך).

מכניסים את התוכנית לספק:

Kotlin

var programUri = context.contentResolver.insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
        builder.build().toContentValues())

Java

Uri programUri = context.getContentResolver().insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
      builder.build().toContentValues());

מאחזרים את מזהה התוכנית כדי להשתמש בו בהמשך:

Kotlin

val programId = ContentUris.parseId(programUri)

Java

long programId = ContentUris.parseId(programUri);

הוספת תוכניות לערוץ "הצעות לצפייה"

כדי להוסיף תוכניות לערוץ "הצעות לצפייה", עיינו במאמר הוספת תוכניות לערוץ "הצעות לצפייה".

עדכון תוכנית

אתם יכולים לשנות את פרטי התוכנית. לדוגמה, יכול להיות שתרצו לעדכן את מחיר ההשכרה של סרט, או לעדכן סרגל התקדמות שמראה כמה מהתוכנית המשתמש כבר ראה.

משתמשים ב-PreviewProgram.Builder כדי להגדיר את המאפיינים שרוצים לשנות, ואז מפעילים את getContentResolver().update כדי לעדכן את התוכנית. מציינים את מזהה התוכנית ששמרתם כשהוספתם את התוכנית במקור:

Kotlin

context.contentResolver.update(
        TvContractCompat.buildPreviewProgramUri(programId),
                builder.build().toContentValues(), null, null
)

Java

context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId),
    builder.build().toContentValues(), null, null);

מחיקת תוכנית

Kotlin

context.contentResolver
        .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)

Java

context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);

טיפול בפעולות של משתמשים

האפליקציה יכולה לעזור למשתמשים לגלות תוכן על ידי הצגת ממשק משתמש להצגה ולהוספה של ערוצים. האפליקציה צריכה גם לטפל באינטראקציות עם הערוצים אחרי שהם מופיעים במסך הבית.

חיפוש והוספה של ערוצים

האפליקציה יכולה לספק רכיב בממשק המשתמש שמאפשר למשתמש לבחור ולהוסיף את הערוצים שלה (לדוגמה, כפתור שמבקש להוסיף את הערוץ).

אחרי שהמשתמש מבקש ערוץ ספציפי, מריצים את הקוד הזה כדי לקבל את הרשאת המשתמש להוסיף אותו לממשק המשתמש של מסך הבית:

Kotlin

val intent = Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE)
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId)
try {
  activity.startActivityForResult(intent, 0)
} catch (e: ActivityNotFoundException) {
  // handle error
}

Java

Intent intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
try {
   activity.startActivityForResult(intent, 0);
} catch (ActivityNotFoundException e) {
  // handle error
}

המערכת מציגה תיבת דו-שיח שבה המשתמש מתבקש לאשר את הערוץ. מטפלים בתוצאה של הבקשה ב-method‏ onActivityResult של הפעילות (Activity.RESULT_CANCELED או Activity.RESULT_OK).

אירועים במסך הבית של Android TV

כשהמשתמש יוצר אינטראקציה עם התוכניות והערוצים שפורסמו על ידי האפליקציה, מסך הבית שולח כוונות לאפליקציה:

  • מסך הבית שולח לאפליקציה את ה-Uri שמאוחסן במאפיין APP_LINK_INTENT_URI של הערוץ כשהמשתמש בוחר בלוגו של הערוץ. האפליקציה צריכה פשוט להפעיל את ממשק המשתמש הראשי שלה או תצוגה שקשורה לערוץ שנבחר.
  • מסך הבית שולח לאפליקציה את ה-Uri ששמור במאפיין INTENT_URI של תוכנית כשהמשתמש בוחר תוכנית. האפליקציה אמורה להפעיל את התוכן שנבחר.
  • המשתמשים יכולים לציין שהם כבר לא מעוניינים בתוכנית מסוימת, ושהם רוצים להסיר אותה מממשק המשתמש של מסך הבית. המערכת מסירה את התוכנית מממשק המשתמש ושולחת לאפליקציה שהתוכנית שייכת לה Intent‏ (android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED או android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED) עם מזהה התוכנית. האפליקציה צריכה להסיר את התוכנית מהספק ולא להוסיף אותה מחדש.

חשוב ליצור מסנני Intent לכל ה-Uris שמסך הבית שולח לאינטראקציות של משתמשים. לדוגמה:

<receiver
   android:name=".WatchNextProgramRemoved"
   android:enabled="true"
   android:exported="true">
   <intent-filter>
       <action android:name="android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
   </intent-filter>
</receiver>

שיקולים נוספים

  • בהרבה אפליקציות לטלוויזיה המשתמשים נדרשים להתחבר. במקרה כזה, BroadcastReceiver שמאזין ל-android.media.tv.action.INITIALIZE_PROGRAMS צריך להציע תוכן של ערוצים למשתמשים לא מאומתים. לדוגמה, האפליקציה יכולה להציג בהתחלה את התוכן הכי טוב או תוכן פופולרי כרגע. אחרי שהמשתמש מתחבר, האפליקציה יכולה להציג תוכן בהתאמה אישית. זו הזדמנות מצוינת למכור מוצר או שירות נוסף (upsell) למשתמשים לפני שהם מתחברים.
  • כשהאפליקציה לא פועלת בחזית וצריך לעדכן ערוץ או תוכנית, משתמשים ב-JobScheduler כדי לתזמן את העבודה (ראו JobScheduler ו-JobService).
  • המערכת יכולה לבטל את הרשאות הספק של האפליקציה אם היא לא פועלת כראוי (לדוגמה: שולחת ספאם לספק באופן קבוע). חשוב להקפיד לעטוף את הקוד שניגש לספק בסעיפי try-catch כדי לטפל בחריגות אבטחה.
  • לפני שמעדכנים תוכניות וערוצים, צריך לשלוח שאילתה לספק לגבי הנתונים שצריך לעדכן ולהתאים את הנתונים. לדוגמה, אין צורך לעדכן תוכנית שהמשתמש רוצה להסיר מממשק המשתמש. השתמשו במשימת רקע שמכניסה או מעדכנת את הנתונים שלכם אצל הספק אחרי שליחת שאילתה לגבי הנתונים הקיימים, ואז בקשו אישור לערוצים שלכם. אפשר להריץ את המשימה הזו כשהאפליקציה מופעלת ובכל פעם שצריך לעדכן את הנתונים של האפליקציה.

Kotlin

context.contentResolver
      .query(
          TvContractCompat.buildChannelUri(channelId),
              null, null, null, null).use({
                  cursor-> if (cursor != null and cursor.moveToNext()) {
                                val channel = Channel.fromCursor(cursor)
                                if (channel.isBrowsable()) {
                                    //update channel's programs
                                }
                            }
              })

Java

try (Cursor cursor = context.getContentResolver()
          .query(
              TvContractCompat.buildChannelUri(channelId),
              null,
              null,
              null,
              null)) {
                  if (cursor != null &amp;&amp; cursor.moveToNext()) {
                      Channel channel = Channel.fromCursor(cursor);
                      if (channel.isBrowsable()) {
                          //update channel's programs
                      }
                  }
              }
  • צריך להשתמש במזהי URI ייחודיים לכל התמונות (סמלי לוגו, סמלים, תמונות תוכן). חשוב להשתמש ב-URI אחר כשמעדכנים תמונה. כל התמונות נשמרות במטמון. אם לא תשנו את ה-URI כשתחליפו את התמונה, התמונה הישנה תמשיך להופיע.

  • חשוב לזכור שאסור להשתמש בסעיפי WHERE, וקריאות לספקים עם סעיפי WHERE יגרמו לחריגת אבטחה.

מאפיינים

בקטע הזה מתוארים בנפרד המאפיינים של הערוץ והמאפיינים של התוכנית.

מאפייני הערוץ

צריך לציין את המאפיינים האלה לכל ערוץ:

מאפיין הערות
סוג מוגדרת בתור TYPE_PREVIEW.
DISPLAY_NAME מוגדר לשם הערוץ.
APP_LINK_INTENT_URI כשהמשתמש בוחר את הלוגו של הערוץ, המערכת שולחת Intent כדי להתחיל פעילות שמציגה תוכן שרלוונטי לערוץ. מגדירים את המאפיין הזה ל-URI שמשמש במסנן ה-Intent של הפעילות הזו.

בנוסף, בערוץ יש גם שישה שדות ששמורים לשימוש פנימי באפליקציה. אפשר להשתמש בשדות האלה כדי לאחסן מפתחות או ערכים אחרים שיכולים לעזור לאפליקציה למפות את הערוץ למבנה הנתונים הפנימי שלה:

  • INTERNAL_PROVIDER_ID
  • INTERNAL_PROVIDER_DATA
  • INTERNAL_PROVIDER_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • INTERNAL_PROVIDER_FLAG3
  • INTERNAL_PROVIDER_FLAG4

מאפייני התוכנית

בדפים הבאים מפורטים המאפיינים של כל סוג תוכנית:

קוד לדוגמה

בשיעור ה-Codelab שלנו בנושא מסך הבית מוסבר איך לפתח אפליקציות שפועלות עם מסך הבית ואיך להוסיף ערוצים ותוכניות למסך הבית של Android TV.