1page.title=Working with Channel Data 2page.tags=tv, tif 3helpoutsWidget=true 4 5trainingnavtop=true 6 7@jd:body 8 9<div id="tb-wrapper"> 10<div id="tb"> 11 <h2>This lesson teaches you to</h2> 12 <ol> 13 <li><a href="#permission">Get Permission</a></li> 14 <li><a href="#register">Register Channels in the Database</a></li> 15 <li><a href="#update">Update Channel Data</a></li> 16 <li><a href="#applink">Add App Link Information</a></li> 17 </ol> 18 <h2>Try It Out</h2> 19 <ul> 20 <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs"> 21 TV Input Service sample app</a></li> 22 </ul> 23</div> 24</div> 25 26<p>Your TV input must provide Electronic Program Guide (EPG) data for at least 27one channel in its setup activity. You should also periodically update that 28data, with consideration for the size of the update and the processing thread 29that handles it. Additionally, you can provide app links for channels 30that guide the user to related content and activities. 31This lesson discusses creating and updating channel and program data on the 32system database with these considerations in mind.</p> 33 34<p> </p> 35 36<h2 id="permission">Get Permission</h2> 37 38<p>In order for your TV input to work with EPG data, it must declare the 39read and write permissions in its Android manifest file as follows:</p> 40 41<pre> 42<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" /> 43<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> 44</pre> 45 46<h2 id="register">Register Channels in the Database</h2> 47 48<p>The Android TV system database maintains records of channel data for TV inputs. In your setup 49activity, for each of your channels, you must map your channel data to the following fields of the 50{@link android.media.tv.TvContract.Channels} class:</p> 51 52<ul> 53 <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NAME} - the displayed name of the 54 channel</li> 55 <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER} - the displayed channel 56 number</li> 57 <li>{@link android.media.tv.TvContract.Channels#COLUMN_INPUT_ID} - the ID of the TV input service</li> 58 <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_TYPE} - the channel's service type</li> 59 <li>{@link android.media.tv.TvContract.Channels#COLUMN_TYPE} - the channel's broadcast standard 60 type</li> 61 <li>{@link android.media.tv.TvContract.Channels#COLUMN_VIDEO_FORMAT} - the default video format 62 for the channel</li> 63</ul> 64 65<p>Although the TV input framework is generic enough to handle both traditional broadcast and 66over-the-top (OTT) content without any distinction, you may want to define the following columns in 67addition to those above to better identify traditional broadcast channels:</p> 68 69<ul> 70 <li>{@link android.media.tv.TvContract.Channels#COLUMN_ORIGINAL_NETWORK_ID} - the television 71 network ID</li> 72 <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_ID} - the service ID</li> 73 <li>{@link android.media.tv.TvContract.Channels#COLUMN_TRANSPORT_STREAM_ID} - the transport stream 74 ID</li> 75</ul> 76 77<p>If you want to provide app link details for your channels, you need to 78update some additional fields. For more information on app link fields, see 79<a href="#applink">Add App Link Information</a>. 80 81<p>For internet streaming based TV inputs, assign your own values to the above accordingly so that 82each channel can be identified uniquely.</p> 83 84<p>Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup 85activity map the values to the system database as follows:</p> 86 87<pre> 88ContentValues values = new ContentValues(); 89 90values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber); 91values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName); 92values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId); 93values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId); 94values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId); 95values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat); 96 97Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values); 98</pre> 99 100<p>In the example above, <code>channel</code> is an object which holds channel metadata from the 101backend server.</p> 102 103<h3 id="art">Present Channel and Program Information</h2> 104 105<p>The system TV app presents channel and program information to users as they flip through channels, 106as shown in figure 1. To make sure the channel and program information works with the system TV app's 107channel and program information presenter, follow the guidelines below.</p> 108 109<ol> 110<li><strong>Channel number</strong> ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER}) 111<li><strong>Icon</strong> 112(<a href="guide/topics/manifest/application-element.html#icon"><code>android:icon</code></a> in the 113TV input's manifest)</li> 114<li><strong>Program description</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION}) 115<li><strong>Program title</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})</li> 116<li><strong>Channel logo</strong> ({@link android.media.tv.TvContract.Channels.Logo}) 117 <ul> 118 <li>Use the color #EEEEEE to match the surrounding text</li> 119 <li>Don't include padding 120 </ul></li> 121<li><strong>Poster art</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI}) 122 <ul> 123 <li>Aspect ratio between 16:9 and 4:3</li> 124 </ul> 125</ol> 126 127<img src="{@docRoot}images/tv/channel-info.png" id="figure1"> 128<p class="img-caption"> 129 <strong>Figure 1.</strong> The system TV app channel and program information presenter. 130</p> 131 132<p>The system TV app provides the same information through the program guide, including poster art, 133as shown in figure 2.</p> 134 135<img src="{@docRoot}images/tv/prog-guide.png" id="figure2"> 136<p class="img-caption"> 137 <strong>Figure 2.</strong> The system TV app program guide. 138</p> 139 140<h2 id="update">Update Channel Data</h2> 141 142<p>When updating existing channel data, use the 143{@link android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues, 144java.lang.String, java.lang.String[]) update()} 145method instead of deleting and re-adding the data. You can identify the current version of the data 146by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER} 147and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER} 148when choosing the records to update.</p> 149 150<p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider} 151can take time. Only add current programs (those within two hours of the current time) when you update, 152and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to 153update the rest of the channel data in the background. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java"> 154Android TV Live TV Sample App</a> for an example.</p> 155 156<h3 id="batch">Batch Loading Channel Data</h3> 157 158<p>When updating the system database with a large amount of channel data, use the {@link android.content.ContentResolver} 159{@link android.content.ContentResolver#applyBatch applyBatch()} 160or 161{@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()} 162method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p> 163 164<pre> 165ArrayList<ContentProviderOperation> ops = new ArrayList<>(); 166int programsCount = mChannelInfo.mPrograms.size(); 167for (int j = 0; j < programsCount; ++j) { 168 ProgramInfo program = mChannelInfo.mPrograms.get(j); 169 ops.add(ContentProviderOperation.newInsert( 170 TvContract.Programs.CONTENT_URI) 171 .withValues(programs.get(j)) 172 .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, 173 programStartSec * 1000) 174 .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, 175 (programStartSec + program.mDurationSec) * 1000) 176 .build()); 177 programStartSec = programStartSec + program.mDurationSec; 178 if (j % 100 == 99 || j == programsCount - 1) { 179 try { 180 <strong>getContentResolver().applyBatch(TvContract.AUTHORITY, ops);</strong> 181 } catch (RemoteException | OperationApplicationException e) { 182 Log.e(TAG, "Failed to insert programs.", e); 183 return; 184 } 185 ops.clear(); 186 } 187} 188</pre> 189 190<h3 id="async">Processing Channel Data Asynchronously</h3> 191 192<p>Data manipulation, such as fetching a stream from the server or accessing the database, should 193not block the UI thread. Using an {@link android.os.AsyncTask} is one 194way to perform updates asynchronously. For example, when loading channel info from a backend server, 195you can use {@link android.os.AsyncTask} as follows:</p> 196 197<pre> 198private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void>> { 199 200 private Context mContext; 201 202 public LoadTvInputTask(Context context) { 203 mContext = context; 204 } 205 206 @Override 207 protected Void doInBackground(Uri... uris) { 208 try { 209 fetchUri(uris[0]); 210 } catch (IOException e) { 211 Log.d(“LoadTvInputTask”, “fetchUri error”); 212 } 213 return null; 214 } 215 216 private void fetchUri(Uri videoUri) throws IOException { 217 InputStream inputStream = null; 218 try { 219 inputStream = mContext.getContentResolver().openInputStream(videoUri); 220 XmlPullParser parser = Xml.newPullParser(); 221 try { 222 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); 223 parser.setInput(inputStream, null); 224 sTvInput = ChannelXMLParser.parseTvInput(parser); 225 sSampleChannels = ChannelXMLParser.parseChannelXML(parser); 226 } catch (XmlPullParserException e) { 227 e.printStackTrace(); 228 } 229 } finally { 230 if (inputStream != null) { 231 inputStream.close(); 232 } 233 } 234 } 235} 236</pre> 237 238<p>If you need to update EPG data on a regular basis, consider using 239a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html"> 240Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time, 241such as every day at 3:00 a.m. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java"> 242Android TV live TV sample app</a> for an example.</p> 243 244<p>Other techniques to separate the data update tasks from the UI thread include using the 245{@link android.os.HandlerThread} class, or you may implement your own using {@link android.os.Looper} 246and {@link android.os.Handler} classes. See <a href="{@docRoot}guide/components/processes-and-threads.html"> 247Processes and Threads</a> for more information.</p> 248 249<h2 id="applink">Add App Link Information</h2> 250 251<p>Channels can use <em>app links</em> to let users easily launch a related 252activity while they are watching channel content. Channel apps use 253app links to extend user engagement by launching activities that show 254related information or additional content. For example, you can use app links 255to do the following:</p> 256 257<ul> 258<li>Guide the user to discover and purchase related content.</li> 259<li>Provide additional information about currently playing content.</li> 260<li>While viewing episodic content, start viewing the next episode in a 261series.</li> 262<li>Let the user interact with content—for example, rate or review 263content—without interrupting content playback.</li> 264</ul> 265 266<p>App links are displayed when the user presses <b>Select</b> to show the 267TV menu while watching channel content.</p> 268 269<img alt="" src="{@docRoot}images/training/tv/tif/app-link.png" 270srcset="{@docRoot}images/training/tv/tif/app-link.png 1x, 271{@docRoot}images/training/tv/tif/app-link-2x.png 2x" id="figure1"/> 272<p class="img-caption"><strong>Figure 1.</strong> An example app link 273displayed on the <b>Channels</b> row while channel content is shown.</p> 274 275<p>When the user selects the app link, the system starts an activity using 276an intent URI specified by the channel app. Channel content continues to play 277while the app link activity is active. The user can return to the channel 278content by pressing <b>Back</b>.</p> 279 280<h3 id="card">Provide App Link Channel Data</h4> 281 282<p>Android TV automatically creates an app link for each channel, 283using information from the channel data. To provide app link information, 284specify the following details in your 285{@link android.media.tv.TvContract.Channels} fields: 286</p> 287 288<ul> 289<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_COLOR} - The 290accent color of the app link for this channel. For an example accent color, 291see figure 2, callout 3. 292</li> 293<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_ICON_URI} - 294The URI for the app badge icon of the app link for this channel. For an 295example app badge icon, see figure 2, callout 2. 296</li> 297<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_INTENT_URI} - 298The intent URI of the app link for this channel. You can create the URI 299using {@link android.content.Intent#toUri(int) toUri(int)} with 300{@link android.content.Intent#URI_INTENT_SCHEME URI_INTENT_SCHEME} and 301convert the URI back to the original intent with 302{@link android.content.Intent#parseUri parseUri()}. 303</li> 304<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_POSTER_ART_URI} 305- The URI for the poster art used as the background of the app link 306for this channel. For an example poster image, see figure 2, callout 1.</li> 307<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_TEXT} - 308The descriptive link text of the app link for this channel. For an example 309app link description, see the text in figure 2, callout 3.</li> 310</ul> 311 312<img alt="" src="{@docRoot}images/training/tv/tif/app-link-diagram.png"/> 313<p class="img-caption"><strong>Figure 2.</strong> App link details.</p> 314 315<p>If the channel data doesn't specify app link information, the system 316creates a default app link. The system chooses default details as follows:</p> 317 318<ul> 319<li>For the intent URI 320({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_INTENT_URI}), 321the system uses the {@link android.content.Intent#ACTION_MAIN ACTION_MAIN} 322activity for the {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER 323CATEGORY_LEANBACK_LAUNCHER} category, typically defined in the app manifest. 324If this activity is not defined, a non-functioning app link appears—if 325the user clicks it, nothing happens.</li> 326<li>For the descriptive text 327({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_TEXT}), the system 328uses "Open <var>app-name</var>". If no viable app link intent URI is defined, 329the system uses "No link available".</li> 330<li>For the accent color 331({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_COLOR}), 332the system uses the default app color.</li> 333<li>For the poster image 334({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_POSTER_ART_URI}), 335the system uses the app's home screen banner. If the app doesn't provide a 336banner, the system uses a default TV app image.</li> 337<li>For the badge icon 338({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_ICON_URI}), the 339system uses a badge that shows the app name. If the system is also using the 340app banner or default app image for the poster image, no app badge is shown. 341</li> 342</ul> 343 344<p>You specify app link details for your channels in your app's 345setup activity. You can update these app link details at any point, so 346if an app link needs to match channel changes, update app 347link details and call 348{@link android.content.ContentResolver#update(android.net.Uri, 349android.content.ContentValues, java.lang.String, java.lang.String[]) 350ContentResolver.update()} as needed. For more details on updating 351channel data, see <a href="#update">Update Channel Data</a>. 352</p> 353 354 355 356