Home | History | Annotate | Download | only in tif
      1 page.title=Working with Channel Data
      2 page.tags=tv, tif
      3 helpoutsWidget=true
      4 
      5 trainingnavtop=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
     26 setup activity. You should also periodically update that data, with consideration for the size of
     27 the update and the processing thread that handles it. This lesson discusses creating and updating
     28 channel 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
     35 read 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
     45 activity, 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
     62 over-the-top (OTT) content without any distinction, you may want to define the following columns in
     63 addition 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
     74 each 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
     77 activity map the values to the system database as follows:</p>
     78 
     79 <pre>
     80 ContentValues values = new ContentValues();
     81 
     82 values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber);
     83 values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName);
     84 values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId);
     85 values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId);
     86 values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId);
     87 values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat);
     88 
     89 Uri 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
     93 backend 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,
     98 as shown in figure 1. To make sure the channel and program information works with the system TV app's
     99 channel 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
    105 TV 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,
    125 as 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,
    136 java.lang.String, java.lang.String[]) update()}
    137 method instead of deleting and re-adding the data. You can identify the current version of the data
    138 by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER}
    139 and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER}
    140 when choosing the records to update.</p>
    141 
    142 <p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider}
    143 can take time. Only add current programs (those within two hours of the current time) when you update,
    144 and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to
    145 update 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">
    146 Android 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()}
    152 or
    153 {@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()}
    154 method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p>
    155 
    156 <pre>
    157 ArrayList&lt;ContentProviderOperation&gt; ops = new ArrayList&lt;&gt;();
    158 int programsCount = mChannelInfo.mPrograms.size();
    159 for (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
    185 not block the UI thread. Using an {@link android.os.AsyncTask} is one
    186 way to perform updates asynchronously.  For example, when loading channel info from a backend server,
    187 you can use {@link android.os.AsyncTask} as follows:</p>
    188 
    189 <pre>
    190 private 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
    231 a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">
    232 Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time,
    233 such 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">
    234 Android 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}
    238 and {@link android.os.Handler} classes.  See <a href="{@docRoot}guide/components/processes-and-threads.html">
    239 Processes and Threads</a> for more information.</p>