Adding docs for app links for ATV channel apps in updated Channel Data topic. Adding screenshots and diagram of app link card. Bug: 23574268 Change-Id: I00ae953e1ec44352eaedb070c8ffd9bd02b13d7d
356 lines
16 KiB
Plaintext
356 lines
16 KiB
Plaintext
page.title=Working with Channel Data
|
|
page.tags=tv, tif
|
|
helpoutsWidget=true
|
|
|
|
trainingnavtop=true
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#permission">Get Permission</a></li>
|
|
<li><a href="#register">Register Channels in the Database</a></li>
|
|
<li><a href="#update">Update Channel Data</a></li>
|
|
<li><a href="#applink">Add App Link Information</a></li>
|
|
</ol>
|
|
<h2>Try It Out</h2>
|
|
<ul>
|
|
<li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
|
|
TV Input Service sample app</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<p>Your TV input must provide Electronic Program Guide (EPG) data for at least
|
|
one channel in its setup activity. You should also periodically update that
|
|
data, with consideration for the size of the update and the processing thread
|
|
that handles it. Additionally, you can provide app links for channels
|
|
that guide the user to related content and activities.
|
|
This lesson discusses creating and updating channel and program data on the
|
|
system database with these considerations in mind.</p>
|
|
|
|
<p> </p>
|
|
|
|
<h2 id="permission">Get Permission</h2>
|
|
|
|
<p>In order for your TV input to work with EPG data, it must declare the
|
|
read and write permissions in its Android manifest file as follows:</p>
|
|
|
|
<pre>
|
|
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
|
|
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
|
|
</pre>
|
|
|
|
<h2 id="register">Register Channels in the Database</h2>
|
|
|
|
<p>The Android TV system database maintains records of channel data for TV inputs. In your setup
|
|
activity, for each of your channels, you must map your channel data to the following fields of the
|
|
{@link android.media.tv.TvContract.Channels} class:</p>
|
|
|
|
<ul>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NAME} - the displayed name of the
|
|
channel</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER} - the displayed channel
|
|
number</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_INPUT_ID} - the ID of the TV input service</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_TYPE} - the channel's service type</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_TYPE} - the channel's broadcast standard
|
|
type</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_VIDEO_FORMAT} - the default video format
|
|
for the channel</li>
|
|
</ul>
|
|
|
|
<p>Although the TV input framework is generic enough to handle both traditional broadcast and
|
|
over-the-top (OTT) content without any distinction, you may want to define the following columns in
|
|
addition to those above to better identify traditional broadcast channels:</p>
|
|
|
|
<ul>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_ORIGINAL_NETWORK_ID} - the television
|
|
network ID</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_ID} - the service ID</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_TRANSPORT_STREAM_ID} - the transport stream
|
|
ID</li>
|
|
</ul>
|
|
|
|
<p>If you want to provide app link details for your channels, you need to
|
|
update some additional fields. For more information on app link fields, see
|
|
<a href="#applink">Add App Link Information</a>.
|
|
|
|
<p>For internet streaming based TV inputs, assign your own values to the above accordingly so that
|
|
each channel can be identified uniquely.</p>
|
|
|
|
<p>Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup
|
|
activity map the values to the system database as follows:</p>
|
|
|
|
<pre>
|
|
ContentValues values = new ContentValues();
|
|
|
|
values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber);
|
|
values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName);
|
|
values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId);
|
|
values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId);
|
|
values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId);
|
|
values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat);
|
|
|
|
Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
|
|
</pre>
|
|
|
|
<p>In the example above, <code>channel</code> is an object which holds channel metadata from the
|
|
backend server.</p>
|
|
|
|
<h3 id="art">Present Channel and Program Information</h2>
|
|
|
|
<p>The system TV app presents channel and program information to users as they flip through channels,
|
|
as shown in figure 1. To make sure the channel and program information works with the system TV app's
|
|
channel and program information presenter, follow the guidelines below.</p>
|
|
|
|
<ol>
|
|
<li><strong>Channel number</strong> ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER})
|
|
<li><strong>Icon</strong>
|
|
(<a href="guide/topics/manifest/application-element.html#icon"><code>android:icon</code></a> in the
|
|
TV input's manifest)</li>
|
|
<li><strong>Program description</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION})
|
|
<li><strong>Program title</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})</li>
|
|
<li><strong>Channel logo</strong> ({@link android.media.tv.TvContract.Channels.Logo})
|
|
<ul>
|
|
<li>Use the color #EEEEEE to match the surrounding text</li>
|
|
<li>Don't include padding
|
|
</ul></li>
|
|
<li><strong>Poster art</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI})
|
|
<ul>
|
|
<li>Aspect ratio between 16:9 and 4:3</li>
|
|
</ul>
|
|
</ol>
|
|
|
|
<img src="{@docRoot}images/tv/channel-info.png" id="figure1">
|
|
<p class="img-caption">
|
|
<strong>Figure 1.</strong> The system TV app channel and program information presenter.
|
|
</p>
|
|
|
|
<p>The system TV app provides the same information through the program guide, including poster art,
|
|
as shown in figure 2.</p>
|
|
|
|
<img src="{@docRoot}images/tv/prog-guide.png" id="figure2">
|
|
<p class="img-caption">
|
|
<strong>Figure 2.</strong> The system TV app program guide.
|
|
</p>
|
|
|
|
<h2 id="update">Update Channel Data</h2>
|
|
|
|
<p>When updating existing channel data, use the
|
|
{@link android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues,
|
|
java.lang.String, java.lang.String[]) update()}
|
|
method instead of deleting and re-adding the data. You can identify the current version of the data
|
|
by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER}
|
|
and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER}
|
|
when choosing the records to update.</p>
|
|
|
|
<p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider}
|
|
can take time. Only add current programs (those within two hours of the current time) when you update,
|
|
and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to
|
|
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">
|
|
Android TV Live TV Sample App</a> for an example.</p>
|
|
|
|
<h3 id="batch">Batch Loading Channel Data</h3>
|
|
|
|
<p>When updating the system database with a large amount of channel data, use the {@link android.content.ContentResolver}
|
|
{@link android.content.ContentResolver#applyBatch applyBatch()}
|
|
or
|
|
{@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()}
|
|
method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p>
|
|
|
|
<pre>
|
|
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
|
int programsCount = mChannelInfo.mPrograms.size();
|
|
for (int j = 0; j < programsCount; ++j) {
|
|
ProgramInfo program = mChannelInfo.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.mDurationSec) * 1000)
|
|
.build());
|
|
programStartSec = programStartSec + program.mDurationSec;
|
|
if (j % 100 == 99 || j == programsCount - 1) {
|
|
try {
|
|
<strong>getContentResolver().applyBatch(TvContract.AUTHORITY, ops);</strong>
|
|
} catch (RemoteException | OperationApplicationException e) {
|
|
Log.e(TAG, "Failed to insert programs.", e);
|
|
return;
|
|
}
|
|
ops.clear();
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<h3 id="async">Processing Channel Data Asynchronously</h3>
|
|
|
|
<p>Data manipulation, such as fetching a stream from the server or accessing the database, should
|
|
not block the UI thread. Using an {@link android.os.AsyncTask} is one
|
|
way to perform updates asynchronously. For example, when loading channel info from a backend server,
|
|
you can use {@link android.os.AsyncTask} as follows:</p>
|
|
|
|
<pre>
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>If you need to update EPG data on a regular basis, consider using
|
|
a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">
|
|
Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time,
|
|
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">
|
|
Android TV live TV sample app</a> for an example.</p>
|
|
|
|
<p>Other techniques to separate the data update tasks from the UI thread include using the
|
|
{@link android.os.HandlerThread} class, or you may implement your own using {@link android.os.Looper}
|
|
and {@link android.os.Handler} classes. See <a href="{@docRoot}guide/components/processes-and-threads.html">
|
|
Processes and Threads</a> for more information.</p>
|
|
|
|
<h2 id="applink">Add App Link Information</h2>
|
|
|
|
<p>Channels can use <em>app links</em> to let users easily launch a related
|
|
activity while they are watching channel content. Channel apps use
|
|
app links to extend user engagement by launching activities that show
|
|
related information or additional content. For example, you can use app links
|
|
to do the following:</p>
|
|
|
|
<ul>
|
|
<li>Guide the user to discover and purchase related content.</li>
|
|
<li>Provide additional information about currently playing content.</li>
|
|
<li>While viewing episodic content, start viewing the next episode in a
|
|
series.</li>
|
|
<li>Let the user interact with content—for example, rate or review
|
|
content—without interrupting content playback.</li>
|
|
</ul>
|
|
|
|
<p>App links are displayed when the user presses <b>Select</b> to show the
|
|
TV menu while watching channel content.</p>
|
|
|
|
<img alt="" src="{@docRoot}images/training/tv/tif/app-link.png"
|
|
srcset="{@docRoot}images/training/tv/tif/app-link.png 1x,
|
|
{@docRoot}images/training/tv/tif/app-link-2x.png 2x" id="figure1"/>
|
|
<p class="img-caption"><strong>Figure 1.</strong> An example app link
|
|
displayed on the <b>Channels</b> row while channel content is shown.</p>
|
|
|
|
<p>When the user selects the app link, the system starts an activity using
|
|
an intent URI specified by the channel app. Channel content continues to play
|
|
while the app link activity is active. The user can return to the channel
|
|
content by pressing <b>Back</b>.</p>
|
|
|
|
<h3 id="card">Provide App Link Channel Data</h4>
|
|
|
|
<p>Android TV automatically creates an app link for each channel,
|
|
using information from the channel data. To provide app link information,
|
|
specify the following details in your
|
|
{@link android.media.tv.TvContract.Channels} fields:
|
|
</p>
|
|
|
|
<ul>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_COLOR} - The
|
|
accent color of the app link for this channel. For an example accent color,
|
|
see figure 2, callout 3.
|
|
</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_ICON_URI} -
|
|
The URI for the app badge icon of the app link for this channel. For an
|
|
example app badge icon, see figure 2, callout 2.
|
|
</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_INTENT_URI} -
|
|
The intent URI of the app link for this channel. You can create the URI
|
|
using {@link android.content.Intent#toUri(int) toUri(int)} with
|
|
{@link android.content.Intent#URI_INTENT_SCHEME URI_INTENT_SCHEME} and
|
|
convert the URI back to the original intent with
|
|
{@link android.content.Intent#parseUri parseUri()}.
|
|
</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_POSTER_ART_URI}
|
|
- The URI for the poster art used as the background of the app link
|
|
for this channel. For an example poster image, see figure 2, callout 1.</li>
|
|
<li>{@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_TEXT} -
|
|
The descriptive link text of the app link for this channel. For an example
|
|
app link description, see the text in figure 2, callout 3.</li>
|
|
</ul>
|
|
|
|
<img alt="" src="{@docRoot}images/training/tv/tif/app-link-diagram.png"/>
|
|
<p class="img-caption"><strong>Figure 2.</strong> App link details.</p>
|
|
|
|
<p>If the channel data doesn't specify app link information, the system
|
|
creates a default app link. The system chooses default details as follows:</p>
|
|
|
|
<ul>
|
|
<li>For the intent URI
|
|
({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_INTENT_URI}),
|
|
the system uses the {@link android.content.Intent#ACTION_MAIN ACTION_MAIN}
|
|
activity for the {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER
|
|
CATEGORY_LEANBACK_LAUNCHER} category, typically defined in the app manifest.
|
|
If this activity is not defined, a non-functioning app link appears—if
|
|
the user clicks it, nothing happens.</li>
|
|
<li>For the descriptive text
|
|
({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_TEXT}), the system
|
|
uses "Open <var>app-name</var>". If no viable app link intent URI is defined,
|
|
the system uses "No link available".</li>
|
|
<li>For the accent color
|
|
({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_COLOR}),
|
|
the system uses the default app color.</li>
|
|
<li>For the poster image
|
|
({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_POSTER_ART_URI}),
|
|
the system uses the app's home screen banner. If the app doesn't provide a
|
|
banner, the system uses a default TV app image.</li>
|
|
<li>For the badge icon
|
|
({@link android.media.tv.TvContract.Channels#COLUMN_APP_LINK_ICON_URI}), the
|
|
system uses a badge that shows the app name. If the system is also using the
|
|
app banner or default app image for the poster image, no app badge is shown.
|
|
</li>
|
|
</ul>
|
|
|
|
<p>You specify app link details for your channels in your app's
|
|
setup activity. You can update these app link details at any point, so
|
|
if an app link needs to match channel changes, update app
|
|
link details and call
|
|
{@link android.content.ContentResolver#update(android.net.Uri,
|
|
android.content.ContentValues, java.lang.String, java.lang.String[])
|
|
ContentResolver.update()} as needed. For more details on updating
|
|
channel data, see <a href="#update">Update Channel Data</a>.
|
|
</p>
|
|
|
|
|
|
|