קלט הטלוויזיה חייב לספק נתונים של מדריך התוכניות האלקטרוני (EPG) למשך ערוץ אחד בפעילות ההגדרה שלו. כדאי גם לעדכן מדי פעם את הפרטים הבאים תוך התייחסות לגודל העדכון ולשרשור העיבוד שמטפל בזה. בנוסף, אפשר לספק קישורים לאפליקציה לערוצים שינחו את המשתמשים לפעילויות ולתכנים קשורים. השיעור הזה עוסק ביצירה ובעדכון של נתונים לגבי ערוצים ותוכנית את מסד הנתונים של המערכת מתוך מחשבה על השיקולים האלה.
אפשר לנסות את אפליקציה לדוגמה של TV קלט Service.
לקבלת הרשאה
כדי שקלט הטלוויזיה יפעל עם נתוני EPG, עליו להצהיר על הרשאת כתיבה בקובץ המניפסט של Android באופן הבא:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
רישום ערוצים במסד הנתונים
מסד הנתונים של המערכת של Android TV שומר רשומות של נתוני הערוץ עבור קלט של טלוויזיה. בהגדרה
עליכם למפות את נתוני הערוץ לשדות הבאים של כל אחד מהערוצים שלכם,
כיתה אחת (TvContract.Channels
):
COLUMN_DISPLAY_NAME
- השם המוצג של ערוץCOLUMN_DISPLAY_NUMBER
– הערוץ המוצג מספר טלפוןCOLUMN_INPUT_ID
– המזהה של שירות הקלט של הטלוויזיהCOLUMN_SERVICE_TYPE
– סוג השירות של הערוץCOLUMN_TYPE
- תקן השידור של הערוץ ההמרהCOLUMN_VIDEO_FORMAT
- פורמט ברירת המחדל לסרטונים לערוץ
למרות שמסגרת הקלט של הטלוויזיה היא כללית מספיק כדי לטפל גם בשידורים רגילים וגם תוכן 'ישירות ללקוח' (OTT) ללא הבחנה, מומלץ להגדיר את העמודות הבאות בנוסף לאלו שצוינו למעלה, כדי לזהות טוב יותר ערוצי שידור מסורתיים:
COLUMN_ORIGINAL_NETWORK_ID
– הטלוויזיה מזהה רשתCOLUMN_SERVICE_ID
– מזהה השירותCOLUMN_TRANSPORT_STREAM_ID
- זרם התעבורה ID [מזהה]
אם ברצונך לספק את פרטי הקישור לאפליקציה עבור הערוצים שלך, עליך: לעדכן שדות נוספים. למידע נוסף על שדות קישור לאפליקציות: מוסיפים מידע על קישור לאפליקציה.
לכניסות טלוויזיה שמבוססות על סטרימינג באינטרנט, מקצים את הערכים שלכם לערכים שלמעלה בהתאם, ניתן לזהות כל ערוץ באופן ייחודי.
שולפים את המטא-נתונים של הערוץ (ב-XML, JSON או כל דבר אחר) משרת הקצה העורפי ובהגדרה מיפוי הערכים למסד הנתונים של המערכת באופן הבא:
Kotlin
val values = ContentValues().apply { put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number) put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name) put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId) put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId) put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId) put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat) } val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)
Java
ContentValues values = new ContentValues(); values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number); values.put(Channels.COLUMN_DISPLAY_NAME, channel.name); values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId); values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId); values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId); values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat); Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
בדוגמה שלמעלה, channel
הוא אובייקט שמכיל מטא-נתונים של ערוץ
שרת עורפי.
הצגת מידע על הערוץ והתוכנית
אפליקציית המערכת של YouTube TV מציגה למשתמשים מידע על הערוצים והתוכניות בזמן שהם מעיינים בערוצים, כפי שמתואר באיור 1. כדי לוודא שפרטי הערוץ והתוכנית פועלים עם אם אתם מציגים את פרטי הערוץ והתוכנית, עליכם לפעול לפי ההנחיות הבאות.
- מספר הערוץ (
COLUMN_DISPLAY_NUMBER
) - סמל
(
android:icon
ב מניפסט של קלט טלוויזיה) - תיאור המועדון (
COLUMN_SHORT_DESCRIPTION
) - שם המועדון (
COLUMN_TITLE
) - הלוגו של הערוץ (
TvContract.Channels.Logo
)- צריך להשתמש בצבע #EEEEEE כדי להתאים לטקסט שמסביב
- אין לכלול מרווח פנימי
- עיצוב פוסטר (
COLUMN_POSTER_ART_URI
)- יחס גובה-רוחב בין 16:9 ל-4:3
אפליקציית המערכת של YouTube TV מספקת את אותו מידע דרך מדריך התוכניות, כולל פוסטר, כפי שמתואר באיור 2.
עדכון נתוני הערוץ
כאשר אתם מעדכנים נתוני ערוצים קיימים, השתמשו
update()
במקום למחוק את הנתונים ולהוסיף אותם מחדש. אפשר לזהות את הגרסה הנוכחית של הנתונים
באמצעות Channels.COLUMN_VERSION_NUMBER
ו-Programs.COLUMN_VERSION_NUMBER
כשבוחרים את הרשומות לעדכון.
הערה: אנחנו מוסיפים את נתוני הערוץ אל ContentProvider
יכול לקחת זמן. מוסיפים את התוכניות הנוכחיות (תוכניות בשעתיים מהשעה הנוכחית)
רק כשהגדרת את EpgSyncJobService
לעדכון השאר
של נתוני הערוץ ברקע. צפייה
ה
למשל, אפליקציית Android TV Live TV Sample.
טעינת נתוני ערוצים באצווה
כשמעדכנים את מסד הנתונים של המערכת עם כמות גדולה של נתוני ערוץ, צריך להשתמש בפונקציה ContentResolver
applyBatch()
או
bulkInsert()
. הנה דוגמה באמצעות applyBatch()
:
Kotlin
val ops = ArrayList<ContentProviderOperation>() val programsCount = channelInfo.mPrograms.size channelInfo.mPrograms.forEachIndexed { index, program -> ops += ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI).run { withValues(programs[index]) withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) withValue( TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000 ) build() } programStartSec += program.durationSec if (index % 100 == 99 || index == programsCount - 1) { try { contentResolver.applyBatch(TvContract.AUTHORITY, ops) } catch (e: RemoteException) { Log.e(TAG, "Failed to insert programs.", e) return } catch (e: OperationApplicationException) { Log.e(TAG, "Failed to insert programs.", e) return } ops.clear() } }
Java
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); int programsCount = channelInfo.mPrograms.size(); for (int j = 0; j < programsCount; ++j) { ProgramInfo program = channelInfo.mPrograms.get(j); ops.add(ContentProviderOperation.newInsert( TvContract.Programs.CONTENT_URI) .withValues(programs.get(j)) .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000) .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, (programStartSec + program.durationSec) * 1000) .build()); programStartSec = programStartSec + program.durationSec; if (j % 100 == 99 || j == programsCount - 1) { try { getContentResolver().applyBatch(TvContract.AUTHORITY, ops); } catch (RemoteException | OperationApplicationException e) { Log.e(TAG, "Failed to insert programs.", e); return; } ops.clear(); } }
עיבוד נתוני הערוץ באופן אסינכרוני
ביצוע מניפולציות על נתונים, כמו אחזור זרם מהשרת או גישה למסד הנתונים,
לא לחסום את ה-thread של ממשק המשתמש. השימוש ב-AsyncTask
הוא אחד
לבצע עדכונים באופן אסינכרוני. לדוגמה, כשטוענים את פרטי הערוץ משרת עורפי,
אפשר להשתמש ב-AsyncTask
באופן הבא:
Kotlin
private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() { override fun doInBackground(vararg uris: Uri) { try { fetchUri(uris[0]) } catch (e: IOException) { Log.d("LoadTvInputTask", "fetchUri error") } } @Throws(IOException::class) private fun fetchUri(videoUri: Uri) { context.contentResolver.openInputStream(videoUri).use { inputStream -> Xml.newPullParser().also { parser -> try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(inputStream, null) sTvInput = ChannelXMLParser.parseTvInput(parser) sSampleChannels = ChannelXMLParser.parseChannelXML(parser) } catch (e: XmlPullParserException) { e.printStackTrace() } } } } }
Java
private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> { private Context mContext; public LoadTvInputTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... uris) { try { fetchUri(uris[0]); } catch (IOException e) { Log.d("LoadTvInputTask", "fetchUri error"); } return null; } private void fetchUri(Uri videoUri) throws IOException { InputStream inputStream = null; try { inputStream = mContext.getContentResolver().openInputStream(videoUri); XmlPullParser parser = Xml.newPullParser(); try { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(inputStream, null); sTvInput = ChannelXMLParser.parseTvInput(parser); sSampleChannels = ChannelXMLParser.parseChannelXML(parser); } catch (XmlPullParserException e) { e.printStackTrace(); } } finally { if (inputStream != null) { inputStream.close(); } } } }
אם עליך לעדכן את נתוני EPG באופן קבוע, כדאי לשקול להשתמש
WorkManager
כדי להריץ את תהליך העדכון בזמן חוסר פעילות, למשל כל יום בשעה 3:00 בבוקר.
שיטות אחרות להפרדת משימות עדכון הנתונים מהשרשור ב-UI:
HandlerThread
כיתה, או שאפשר להטמיע משלך באמצעות Looper
ו-Handler
כיתות. ראו
תהליכים ושרשורים למידע נוסף.
הוספת פרטים על קישור לאפליקציה
ערוצים יכולים להשתמש בקישורים לאפליקציות כדי לאפשר למשתמשים להפעיל בקלות קמפיין קשור פעילות בזמן שהם צופים בתוכן של הערוץ. שימוש באפליקציות ערוץ קישורים לאפליקציה כדי להגדיל את מעורבות המשתמשים על ידי הפעלת פעילויות מידע קשור או תוכן נוסף. לדוגמה, אפשר להשתמש בקישורים לאפליקציות לבצע את הפעולות הבאות:
- להנחות את המשתמשים לגלות תוכן קשור ולרכוש אותו.
- ניתן לספק מידע נוסף על התוכן שמופעל כרגע.
- בזמן הצפייה בתוכן שמתייחס לפרקים, מתחילים לצפות בפרק הבא סדרות.
- מתן אפשרות למשתמש ליצור אינטראקציה עם התוכן — לדוגמה, דירוג או כתיבת ביקורת תוכן — בלי להפריע להפעלת התוכן.
קישורים לאפליקציות מוצגים כשהמשתמש לוחץ על בחירה כדי להציג את תפריט טלוויזיה בזמן צפייה בתוכן של ערוץ.
כשהמשתמש לוחץ על הקישור לאפליקציה, המערכת מתחילה פעילות באמצעות URI של Intent שצוין על ידי אפליקציית הערוץ. תוכן הערוץ ממשיך לפעול כשהפעילות של הקישור לאפליקציה פעילה. המשתמש יכול לחזור לערוץ לוחצים על הקודם.
שליחת נתונים לגבי ערוץ של קישור לאפליקציה
מערכת Android TV יוצרת באופן אוטומטי קישור לאפליקציה עבור כל ערוץ,
על סמך מידע מנתוני הערוץ. כדי לספק מידע על קישור לאפליקציה:
צריך לציין את הפרטים הבאים
TvContract.Channels
שדות:
COLUMN_APP_LINK_COLOR
– הצבע המשני של הקישור לאפליקציה בערוץ הזה. כדי לראות דוגמה לצבע משני: ראו איור 2, הסבר 3.COLUMN_APP_LINK_ICON_URI
– ה-URI של סמל תג האפליקציה של הקישור לאפליקציה עבור הערוץ הזה. עבור סמל של תג אפליקציה לדוגמה, ראו איור 2, יתרונות מרכזיים 2.COLUMN_APP_LINK_INTENT_URI
– ה-URI של ה-Intent של הקישור לאפליקציה עבור הערוץ הזה. אפשר ליצור את ה-URI שימוש ב-toUri(int)
עםURI_INTENT_SCHEME
והקבוצה ממירים את ה-URI בחזרה ל-Intent המקורי עםparseUri()
.COLUMN_APP_LINK_POSTER_ART_URI
- ה-URI של הפוסטר שמשמש כרקע של הקישור לאפליקציה לערוץ הזה. לתמונה של פוסטר לדוגמה, ראו איור 2, הסבר 1.COLUMN_APP_LINK_TEXT
– טקסט הקישור התיאורי של הקישור לאפליקציה בערוץ הזה. לדוגמה תיאור הקישור לאפליקציה, לראות את הטקסט בתרשים 2, יתרונות מרכזיים 3.
אם נתוני הערוץ לא מציינים פרטי קישור לאפליקציה, המערכת יוצר קישור ברירת מחדל לאפליקציה. המערכת בוחרת את פרטי ברירת המחדל באופן הבא:
- ל-Intent ה-URI
(
COLUMN_APP_LINK_INTENT_URI
), המערכת משתמשת ב-ACTION_MAIN
פעילות לקטגוריהCATEGORY_LEANBACK_LAUNCHER
, שמוגדרת בדרך כלל בקובץ המניפסט של האפליקציה. אם הפעילות הזו לא מוגדרת, יופיע קישור לא פעיל לאפליקציה, אם שהמשתמש לוחץ עליו, לא קורה כלום. - לטקסט התיאורי
(
COLUMN_APP_LINK_TEXT
), המערכת משתמש באפשרות 'פתיחת app-name'. אם לא מוגדר URI קיים של Intent לקישור לאפליקציה, המערכת משתמשת באפשרות 'אין קישור זמין'. - לצבע המשני
(
COLUMN_APP_LINK_COLOR
), המערכת משתמשת בצבע ברירת המחדל של האפליקציה. - לגבי תמונת הפוסטר
(
COLUMN_APP_LINK_POSTER_ART_URI
), המערכת משתמשת בבאנר של האפליקציה במסך הבית. אם האפליקציה לא מספקת מודעת באנר, המערכת משתמשת בתמונת ברירת מחדל של אפליקציית טלוויזיה. - לסמל התג
(
COLUMN_APP_LINK_ICON_URI
), מערכת משתמשת בתג שמציג את שם האפליקציה. אם המערכת משתמשת גם באנר של אפליקציה או תמונת ברירת מחדל של האפליקציה לתמונת הפוסטר, לא מוצג תג אפליקציה.
מציינים את פרטי הקישור לאפליקציה בערוצים של האפליקציה
פעילות הגדרה. תמיד אפשר לעדכן את פרטי הקישור לאפליקציה, כדי
אם הקישור לאפליקציה צריך להתאים לשינויים בערוץ, צריך לעדכן את האפליקציה
פרטי הקישור והשיחה
ContentResolver.update()
לפי הצורך. לפרטים נוספים על העדכון
נתוני הערוץ, כדאי לעיין במאמר עדכון נתוני הערוץ.