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  </ol>
17  <h2>Try It Out</h2>
18  <ul>
19    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
20      TV Input Service sample app</a></li>
21  </ul>
22</div>
23</div>
24
25<p>Your TV input must provide Electronic Program Guide (EPG) data for at least one channel in its
26setup activity. You should also periodically update that data, with consideration for the size of
27the update and the processing thread that handles it. This lesson discusses creating and updating
28channel and program data on the system database with these considerations in mind.</p>
29
30<p>&nbsp;</p>
31
32<h2 id="permission">Get Permission</h2>
33
34<p>In order for your TV input to work with EPG data, it must declare the
35read and write permissions in its Android manifest file as follows:</p>
36
37<pre>
38&lt;uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" /&gt;
39&lt;uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /&gt;
40</pre>
41
42<h2 id="register">Register Channels in the Database</h2>
43
44<p>The Android TV system database maintains records of channel data for TV inputs. In your setup
45activity, for each of your channels, you must map your channel data to the following fields of the
46{@link android.media.tv.TvContract.Channels} class:</p>
47
48<ul>
49  <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NAME} - the displayed name of the
50  channel</li>
51  <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER} - the displayed channel
52  number</li>
53  <li>{@link android.media.tv.TvContract.Channels#COLUMN_INPUT_ID} - the ID of the TV input service</li>
54  <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_TYPE} - the channel's service type</li>
55  <li>{@link android.media.tv.TvContract.Channels#COLUMN_TYPE} - the channel's broadcast standard
56  type</li>
57  <li>{@link android.media.tv.TvContract.Channels#COLUMN_VIDEO_FORMAT} - the default video format
58  for the channel</li>
59</ul>
60
61<p>Although the TV input framework is generic enough to handle both traditional broadcast and
62over-the-top (OTT) content without any distinction, you may want to define the following columns in
63addition to those above to better identify traditional broadcast channels:</p>
64
65<ul>
66  <li>{@link android.media.tv.TvContract.Channels#COLUMN_ORIGINAL_NETWORK_ID} - the television
67  network ID</li>
68  <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_ID} - the service ID</li>
69  <li>{@link android.media.tv.TvContract.Channels#COLUMN_TRANSPORT_STREAM_ID} - the transport stream
70  ID</li>
71</ul>
72
73<p>For internet streaming based TV inputs, assign your own values to the above accordingly so that
74each channel can be identified uniquely.</p>
75
76<p>Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup
77activity map the values to the system database as follows:</p>
78
79<pre>
80ContentValues values = new ContentValues();
81
82values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber);
83values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName);
84values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId);
85values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId);
86values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId);
87values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat);
88
89Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
90</pre>
91
92<p>In the example above, <code>channel</code> is an object which holds channel metadata from the
93backend server.</p>
94
95<h3 id="art">Present Channel and Program Information</h2>
96
97<p>The system TV app presents channel and program information to users as they flip through channels,
98as shown in figure 1. To make sure the channel and program information works with the system TV app's
99channel and program information presenter, follow the guidelines below.</p>
100
101<ol>
102<li><strong>Channel number</strong> ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER})
103<li><strong>Icon</strong>
104(<a href="guide/topics/manifest/application-element.html#icon"><code>android:icon</code></a> in the
105TV input's manifest)</li>
106<li><strong>Program description</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION})
107<li><strong>Program title</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})</li>
108<li><strong>Channel logo</strong> ({@link android.media.tv.TvContract.Channels.Logo})
109  <ul>
110    <li>Use the color #EEEEEE to match the surrounding text</li>
111    <li>Don't include padding
112  </ul></li>
113<li><strong>Poster art</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI})
114  <ul>
115    <li>Aspect ratio between 16:9 and 4:3</li>
116  </ul>
117</ol>
118
119<img src="{@docRoot}images/tv/channel-info.png" id="figure1">
120<p class="img-caption">
121  <strong>Figure 1.</strong> The system TV app channel and program information presenter.
122</p>
123
124<p>The system TV app provides the same information through the program guide, including poster art,
125as shown in figure 2.</p>
126
127<img src="{@docRoot}images/tv/prog-guide.png" id="figure2">
128<p class="img-caption">
129  <strong>Figure 2.</strong> The system TV app program guide.
130</p>
131
132<h2 id="update">Update Channel Data</h2>
133
134<p>When updating existing channel data, use the
135{@link android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues,
136java.lang.String, java.lang.String[]) update()}
137method instead of deleting and re-adding the data. You can identify the current version of the data
138by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER}
139and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER}
140when choosing the records to update.</p>
141
142<p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider}
143can take time. Only add current programs (those within two hours of the current time) when you update,
144and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to
145update 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">
146Android TV Live TV Sample App</a> for an example.</p>
147
148<h3 id="batch">Batch Loading Channel Data</h3>
149
150<p>When updating the system database with a large amount of channel data, use the {@link android.content.ContentResolver}
151{@link android.content.ContentResolver#applyBatch applyBatch()}
152or
153{@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()}
154method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p>
155
156<pre>
157ArrayList&lt;ContentProviderOperation&gt; ops = new ArrayList&lt;&gt;();
158int programsCount = mChannelInfo.mPrograms.size();
159for (int j = 0; j &lt; programsCount; ++j) {
160    ProgramInfo program = mChannelInfo.mPrograms.get(j);
161    ops.add(ContentProviderOperation.newInsert(
162            TvContract.Programs.CONTENT_URI)
163            .withValues(programs.get(j))
164            .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS,
165                    programStartSec * 1000)
166            .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS,
167                    (programStartSec + program.mDurationSec) * 1000)
168            .build());
169    programStartSec = programStartSec + program.mDurationSec;
170    if (j % 100 == 99 || j == programsCount - 1) {
171        try {
172            <strong>getContentResolver().applyBatch(TvContract.AUTHORITY, ops);</strong>
173        } catch (RemoteException | OperationApplicationException e) {
174            Log.e(TAG, "Failed to insert programs.", e);
175            return;
176        }
177        ops.clear();
178    }
179}
180</pre>
181
182<h3 id="async">Processing Channel Data Asynchronously</h3>
183
184<p>Data manipulation, such as fetching a stream from the server or accessing the database, should
185not block the UI thread. Using an {@link android.os.AsyncTask} is one
186way to perform updates asynchronously.  For example, when loading channel info from a backend server,
187you can use {@link android.os.AsyncTask} as follows:</p>
188
189<pre>
190private static class LoadTvInputTask extends AsyncTask&lt;Uri, Void, Void>&gt; {
191
192    private Context mContext;
193
194    public LoadTvInputTask(Context context) {
195        mContext = context;
196    }
197
198    &#64;Override
199    protected Void doInBackground(Uri... uris) {
200        try {
201            fetchUri(uris[0]);
202        } catch (IOException e) {
203          Log.d(“LoadTvInputTask”, “fetchUri error”);
204        }
205        return null;
206    }
207
208    private void fetchUri(Uri videoUri) throws IOException {
209        InputStream inputStream = null;
210        try {
211            inputStream = mContext.getContentResolver().openInputStream(videoUri);
212            XmlPullParser parser = Xml.newPullParser();
213            try {
214                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
215                parser.setInput(inputStream, null);
216                sTvInput = ChannelXMLParser.parseTvInput(parser);
217                sSampleChannels = ChannelXMLParser.parseChannelXML(parser);
218            } catch (XmlPullParserException e) {
219                e.printStackTrace();
220            }
221        } finally {
222            if (inputStream != null) {
223                inputStream.close();
224            }
225        }
226    }
227}
228</pre>
229
230<p>If you need to update EPG data on a regular basis, consider using
231a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">
232Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time,
233such 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">
234Android TV live TV sample app</a> for an example.</p>
235
236<p>Other techniques to separate the data update tasks from the UI thread include using the
237{@link android.os.HandlerThread} class, or you may implement your own using {@link android.os.Looper}
238and {@link android.os.Handler} classes.  See <a href="{@docRoot}guide/components/processes-and-threads.html">
239Processes and Threads</a> for more information.</p>