diff --git a/docs/html/images/tv/channel-info.png b/docs/html/images/tv/channel-info.png new file mode 100644 index 0000000000000..5a480781dc04e Binary files /dev/null and b/docs/html/images/tv/channel-info.png differ diff --git a/docs/html/images/tv/do-not-attempt.png b/docs/html/images/tv/do-not-attempt.png new file mode 100644 index 0000000000000..18a8775be0d95 Binary files /dev/null and b/docs/html/images/tv/do-not-attempt.png differ diff --git a/docs/html/images/tv/prog-guide.png b/docs/html/images/tv/prog-guide.png new file mode 100644 index 0000000000000..caa2278753600 Binary files /dev/null and b/docs/html/images/tv/prog-guide.png differ diff --git a/docs/html/images/tv/tvinput-life.png b/docs/html/images/tv/tvinput-life.png new file mode 100644 index 0000000000000..fc53f89f6a614 Binary files /dev/null and b/docs/html/images/tv/tvinput-life.png differ diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 11ae1a6f2d670..2873b5b3ad1df 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -998,10 +998,25 @@ include the action bar on devices running Android 2.1 or higher." Building TV Games -
  • - + +
  • diff --git a/docs/html/training/tv/tif/channel.jd b/docs/html/training/tv/tif/channel.jd new file mode 100644 index 0000000000000..999f1ca9c5c5e --- /dev/null +++ b/docs/html/training/tv/tif/channel.jd @@ -0,0 +1,239 @@ +page.title=Working with Channel Data +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +
    + +
    + +

    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. This lesson discusses creating and updating +channel and program data on the system database with these considerations in mind.

    + +

     

    + +

    Get Permission

    + +

    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:

    + +
    +<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
    +<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    +
    + +

    Register Channels in the Database

    + +

    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:

    + + + +

    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:

    + + + +

    For internet streaming based TV inputs, assign your own values to the above accordingly so that +each channel can be identified uniquely.

    + +

    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:

    + +
    +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);
    +
    + +

    In the example above, channel is an object which holds channel metadata from the +backend server.

    + +

    Present Channel and Program Information

    + +

    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.

    + +
      +
    1. Channel number ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER}) +
    2. Icon +(android:icon in the +TV input's manifest)
    3. +
    4. Program description ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION}) +
    5. Program title ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})
    6. +
    7. Channel logo ({@link android.media.tv.TvContract.Channels.Logo}) +
        +
      • Use the color #EEEEEE to match the surrounding text
      • +
      • Don't include padding +
    8. +
    9. Poster art ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI}) +
        +
      • Aspect ratio between 16:9 and 4:3
      • +
      +
    + + +

    + Figure 1. The system TV app channel and program information presenter. +

    + +

    The system TV app provides the same information through the program guide, including poster art, +as shown in figure 2.

    + + +

    + Figure 2. The system TV app program guide. +

    + +

    Update Channel Data

    + +

    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.

    + +

    Note: 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 Sync Adapter to +update the rest of the channel data in the background. See the +Android TV Live TV Sample App for an example.

    + +

    Batch Loading Channel Data

    + +

    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()}:

    + +

    +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 {
    +            getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
    +        } catch (RemoteException | OperationApplicationException e) {
    +            Log.e(TAG, "Failed to insert programs.", e);
    +            return;
    +        }
    +        ops.clear();
    +    }
    +}
    +
    + +

    Processing Channel Data Asynchronously

    + +

    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:

    + +
    +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();
    +            }
    +        }
    +    }
    +}
    +
    + +

    If you need to update EPG data on a regular basis, consider using +a +Sync Adapter 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 +Android TV live TV sample app for an example.

    + +

    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 +Processes and Threads for more information.

    \ No newline at end of file diff --git a/docs/html/training/tv/tif/index.jd b/docs/html/training/tv/tif/index.jd index 9c10850e4cdfc..57392940bee27 100644 --- a/docs/html/training/tv/tif/index.jd +++ b/docs/html/training/tv/tif/index.jd @@ -1,17 +1,26 @@ page.title=Building Live TV Apps page.tags=tv, tif helpoutsWidget=true -page.article=true +startpage=true @jd:body
    +

    Dependencies and Prerequisites

    +
      +
    • Android 5.0 (API level 21) or higher
    • +

    You should also read

    +

    Try It Out

    +
    @@ -44,6 +53,17 @@ page.article=true Building a TV input service for your content can help make it more accessible on TV devices.

    -

    For more information about TV Input Framework, see the -android.media.tv -reference.

    +

    Topics

    + +
    +
    Developing a TV Input Service
    +
    Learn how to develop a TV input service, which works with the system TV app.
    + +
    Working with Channel Data
    +
    Learn how to describe channel and program data for the system.
    + +
    Managing User Interaction
    +
    Learn how to present overlays, manage content availability, and handle content selection.
    +
    + + diff --git a/docs/html/training/tv/tif/tvinput.jd b/docs/html/training/tv/tif/tvinput.jd new file mode 100644 index 0000000000000..91f8ded007bd4 --- /dev/null +++ b/docs/html/training/tv/tif/tvinput.jd @@ -0,0 +1,177 @@ +page.title=Developing a TV Input Service +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +
    + +
    + +

    A TV input service represents a media stream source, and lets you present your media content in a +linear, broadcast TV fashion as channels and programs. With the TV input service, you can provide +parental controls, program guide information, and content ratings. The TV input service works +with the Android system TV app, developed for the device and immutable by third-party apps, which +ultimately controls and presents content on the TV. See + +TV Input Framework for more information about the framework architecture and its components.

    + +

    To develop a TV input service, you implement the following components:

    + + + +

    Declare Your TV Input Service in the Manifest

    + +

    Your app manifest must declare your {@link android.media.tv.TvInputService}. Within that +declaration, specify the {@link android.Manifest.permission#BIND_TV_INPUT} permission to allow the +service to connect the TV input to the system. A system service (TvInputManagerService) +performs the binding and has that permission. The system TV app sends requests to TV input services +via the {@link android.media.tv.TvInputManager} interface. The service declaration must also +include an intent filter that specifies the {@link android.media.tv.TvInputService} +as the action to perform with the intent. Also within the service declaration, declare the service +meta data in a separate XML resource. The service declaration, the intent filter and the service +meta data are described in the following example.

    + +
    +<service android:name="com.example.sampletvinput.SampleTvInput"
    +    android:label="@string/sample_tv_input_label"
    +    android:permission="android.permission.BIND_TV_INPUT">
    +    <intent-filter>
    +        <action android:name="android.media.tv.TvInputService" />
    +    </intent-filter>
    +    <meta-data android:name="android.media.tv.input"
    +      android:resource="@xml/sample_tv_input" />
    +</service>
    +
    + +

    Define the service meta data in separate XML file, as shown in the following example. The service +meta data must include a setup interface that describes the TV input's initial configuration and +channel scan. Also, the service meta data may (optionally) describe a settings activity for users to +modify the TV input's behavior. The service meta data file is located in the XML resources directory +for your application and must match the name of the resource in the manifest. Using the example +manifest entries above, you would create an XML file in the location +res/xml/sample_tv_input.xml, with the following contents:

    + +
    +<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
    +  <!-- Required: activity for setting up the input -->
    +  android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity"
    +  <!-- Optional: activity for controlling the settings -->
    +  android:settingsActivity="com.example.sampletvinput.SampleTvInputSettingsActivity" />
    +
    + +

    Define Your TV Input Service

    + +
    + +

    Figure 1.TvInputService lifecycle.

    +
    + +

    For your service, you extend the {@link android.media.tv.TvInputService} class. A +{@link android.media.tv.TvInputService} implementation is a +bound service where the system service +(TvInputManagerService) is the client that binds to it. The service life cycle methods +you need to implement are illustrated in figure 1.

    + +

    The {@link android.app.Service#onCreate()} method initializes and starts the +{@link android.os.HandlerThread} which provides a process thread separate from the UI thread to +handle system-driven actions. In the following example, the {@link android.app.Service#onCreate()} +method initializes the {@link android.view.accessibility.CaptioningManager} and prepares to handle +the {@link android.media.tv.TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} +and {@link android.media.tv.TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} actions. These +actions describe system intents fired when the user changes the parental control settings, and when +there is a change on the list of blocked ratings.

    + +
    +@Override
    +public void onCreate() {
    +    super.onCreate();
    +    mHandlerThread = new HandlerThread(getClass()
    +      .getSimpleName());
    +    mHandlerThread.start();
    +    mDbHandler = new Handler(mHandlerThread.getLooper());
    +    mHandler = new Handler();
    +    mCaptioningManager = (CaptioningManager)
    +      getSystemService(Context.CAPTIONING_SERVICE);
    +
    +    setTheme(android.R.style.Theme_Holo_Light_NoActionBar);
    +
    +    mSessions = new ArrayList<BaseTvInputSessionImpl>();
    +    IntentFilter intentFilter = new IntentFilter();
    +    intentFilter.addAction(TvInputManager
    +      .ACTION_BLOCKED_RATINGS_CHANGED);
    +    intentFilter.addAction(TvInputManager
    +      .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
    +    registerReceiver(mBroadcastReceiver, intentFilter);
    +}
    +
    + +

    See +Control Content for more information about working with blocked content and providing +parental control. See {@link android.media.tv.TvInputManager} for more system-driven actions that +you may want to handle in your TV input service.

    + +

    The {@link android.media.tv.TvInputService} creates a +{@link android.media.tv.TvInputService.Session} that implements {@link android.os.Handler.Callback} +to handle player state changes. With {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) onSetSurface()}, +the {@link android.media.tv.TvInputService.Session} sets the {@link android.view.Surface} with the +video content. See Integrate Player with Surface +for more information about working with {@link android.view.Surface} to render video.

    + +

    The {@link android.media.tv.TvInputService.Session} handles the +{@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) onTune()} +event when the user selects a channel, and notifies the system TV app for changes in the content and +content meta data. These notify()code> methods are described in + +Control Content and Handle Track Selection further +in this training.

    + +

    Define Setup and Settings Activities

    + +

    The system TV app works with the setup and settings activities you define for your TV input. The +setup activity is required and must provide at least one channel record for the system database. The +system TV app will invoke the setup activity when it cannot find a channel for the TV input. +

    The setup activity describes to the system TV app the channels made available through the TV +input, as demonstrated in the next lesson, Creating +and Updating Channel Data.

    + +

    The settings activity is optional. You can define a settings activity to turn on parental +controls, enable closed captions, set the display attributes, and so forth.

    + + diff --git a/docs/html/training/tv/tif/ui.jd b/docs/html/training/tv/tif/ui.jd new file mode 100644 index 0000000000000..6ead3dbb72f9f --- /dev/null +++ b/docs/html/training/tv/tif/ui.jd @@ -0,0 +1,304 @@ +page.title=Managing User Interaction +page.tags=tv, tif +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +
    + +
    + +

    In the live TV experience the user changes channels and is presented with +channel and program information briefly before the information disappears. Other types of information, +such as messages ("DO NOT ATTEMPT AT HOME"), subtitles, or ads may need to persist. As with any TV +app, such information should not interfere with the program content playing on the screen.

    + + +

    + Figure 1. An overlay message in a live TV app. +

    + +

    Also consider whether certain program content should be presented, given the +content's rating and parental control settings, and how your app behaves and informs the user when +content is blocked or unavailable. This lesson describes how to develop your TV input's user +experience for these considerations.

    + +

    Integrate Player with Surface

    + +

    Your TV input must render video onto a {@link android.view.Surface} object, which is passed by +the {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) TvInputService.Session.onSetSurface()} +method. Here's an example of how to use a {@link android.media.MediaPlayer} instance for playing +content in the {@link android.view.Surface} object:

    + +
    +@Override
    +public boolean onSetSurface(Surface surface) {
    +    if (mPlayer != null) {
    +        mPlayer.setSurface(surface);
    +    }
    +    mSurface = surface;
    +    return true;
    +}
    +
    +@Override
    +public void onSetStreamVolume(float volume) {
    +    if (mPlayer != null) {
    +        mPlayer.setVolume(volume, volume);
    +    }
    +    mVolume = volume;
    +}
    +
    + +

    Similarly, here's how to do it using +ExoPlayer:

    + +
    +@Override
    +public boolean onSetSurface(Surface surface) {
    +    if (mPlayer != null) {
    +        mPlayer.sendMessage(mVideoRenderer,
    +                MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
    +                surface);
    +    }
    +    mSurface = surface;
    +    return true;
    +}
    +
    +@Override
    +public void onSetStreamVolume(float volume) {
    +    if (mPlayer != null) {
    +        mPlayer.sendMessage(mAudioRenderer,
    +                MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
    +                volume);
    +    }
    +    mVolume = volume;
    +}
    +
    + +

    Use an Overlay

    + +

    Use an overlay to display subtitles, messages, ads or MHEG-5 data broadcasts. By default, the +overlay is disabled. You can enable it when you create the session by calling +{@link android.media.tv.TvInputService.Session#setOverlayViewEnabled(boolean) TvInputService.Session.setOverlayViewEnabled(true)}, +as in the following example:

    + +
    +@Override
    +public final Session onCreateSession(String inputId) {
    +    BaseTvInputSessionImpl session = onCreateSessionInternal(inputId);
    +    session.setOverlayViewEnabled(true);
    +    mSessions.add(session);
    +    return session;
    +}
    +
    + +

    Use a {@link android.view.View} object for the overlay, returned from {@link android.media.tv.TvInputService.Session#onCreateOverlayView() TvInputService.Session.onCreateOverlayView()}, as shown here:

    + +
    +@Override
    +public View onCreateOverlayView() {
    +    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    +    View view = inflater.inflate(R.layout.overlayview, null);
    +    mSubtitleView = (SubtitleView) view.findViewById(R.id.subtitles);
    +
    +    // Configure the subtitle view.
    +    CaptionStyleCompat captionStyle;
    +    float captionTextSize = getCaptionFontSize();
    +    captionStyle = CaptionStyleCompat.createFromCaptionStyle(
    +            mCaptioningManager.getUserStyle());
    +    captionTextSize *= mCaptioningManager.getFontScale();
    +    mSubtitleView.setStyle(captionStyle);
    +    mSubtitleView.setTextSize(captionTextSize);
    +    return view;
    +}
    +
    + +

    The layout definition for the overlay might look something like this:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<FrameLayout
    +    xmlns:android="http://schemas.android.com/apk/res/android"
    +    xmlns:tools="http://schemas.android.com/tools"
    +
    +    android:layout_width="match_parent"
    +    android:layout_height="match_parent">
    +
    +    <com.google.android.exoplayer.text.SubtitleView
    +        android:id="@+id/subtitles"
    +        android:layout_width="wrap_content"
    +        android:layout_height="wrap_content"
    +        android:layout_gravity="bottom|center_horizontal"
    +        android:layout_marginLeft="16dp"
    +        android:layout_marginRight="16dp"
    +        android:layout_marginBottom="32dp"
    +        android:visibility="invisible"/>
    +</FrameLayout>
    +
    + +

    Control Content

    + +

    When the user selects a channel, your TV input handles the {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) +onTune()} callback in the {@link android.media.tv.TvInputService.Session} object. The system TV +app's parental controls determine what content displays, given the content rating. +The following sections describe how to manage channel and program selection using the +{@link android.media.tv.TvInputService.Session} notify methods that +communicate with the system TV app.

    + +

    Make Video Unavailable

    + +

    When the user changes the channel, you want to make sure the screen doesn't display any stray +video artifacts before your TV input renders the content. When you call {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) TvInputService.Session.onTune()}, +you can prevent the video from being presented by calling {@link android.media.tv.TvInputService.Session#notifyVideoUnavailable(int) TvInputService.Session.notifyVideoUnavailable()} +and passing the {@link android.media.tv.TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} constant, as +shown in the following example.

    + +
    +@Override
    +public boolean onTune(Uri channelUri) {
    +    if (mSubtitleView != null) {
    +        mSubtitleView.setVisibility(View.INVISIBLE);
    +    }
    +    notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
    +    mUnblockedRatingSet.clear();
    +
    +    mDbHandler.removeCallbacks(mPlayCurrentProgramRunnable);
    +    mPlayCurrentProgramRunnable = new PlayCurrentProgramRunnable(channelUri);
    +    mDbHandler.post(mPlayCurrentProgramRunnable);
    +    return true;
    +}
    +
    + +

    Then, when the content is rendered to the {@link android.view.Surface}, you call +{@link android.media.tv.TvInputService.Session#notifyVideoAvailable() TvInputService.Session.notifyVideoAvailable()} +to allow the video to display, like so:

    + +
    +@Override
    +public void onDrawnToSurface(Surface surface) {
    +    mFirstFrameDrawn = true;
    +    notifyVideoAvailable();
    +}
    +
    + +

    This transition lasts only for fractions of a second, but presenting a blank screen is +visually better than allowing the picture to flash odd blips and jitters.

    + +

    See also, Integrate Player with Surface for more information about working +with {@link android.view.Surface} to render video.

    + +

    Provide Parental Control

    + +

    To determine if a given content is blocked by parental controls and content rating, you check the +{@link android.media.tv.TvInputManager} class methods, {@link android.media.tv.TvInputManager#isParentalControlsEnabled()} +and {@link android.media.tv.TvInputManager#isRatingBlocked(android.media.tv.TvContentRating)}. You +might also want to make sure the content's {@link android.media.tv.TvContentRating} is included in a +set of currently allowed content ratings. These considerations are shown in the following sample.

    + +
    +private void checkContentBlockNeeded() {
    +    if (mCurrentContentRating == null || !mTvInputManager.isParentalControlsEnabled()
    +            || !mTvInputManager.isRatingBlocked(mCurrentContentRating)
    +            || mUnblockedRatingSet.contains(mCurrentContentRating)) {
    +        // Content rating is changed so we don't need to block anymore.
    +        // Unblock content here explicitly to resume playback.
    +        unblockContent(null);
    +        return;
    +    }
    +
    +    mLastBlockedRating = mCurrentContentRating;
    +    if (mPlayer != null) {
    +        // Children restricted content might be blocked by TV app as well,
    +        // but TIF should do its best not to show any single frame of blocked content.
    +        releasePlayer();
    +    }
    +
    +    notifyContentBlocked(mCurrentContentRating);
    +}
    +
    + +

    Once you have determined if the content should or should not be blocked, notify the system TV +app by calling the +{@link android.media.tv.TvInputService.Session} method {@link android.media.tv.TvInputService.Session#notifyContentAllowed() notifyContentAllowed()} +or +{@link android.media.tv.TvInputService.Session#notifyContentBlocked(android.media.tv.TvContentRating) notifyContentBlocked()} +, as shown in the previous example.

    + +

    Use the {@link android.media.tv.TvContentRating} class to generate the system-defined string for +the {@link android.media.tv.TvContract.Programs#COLUMN_CONTENT_RATING} with the +TvContentRating.createRating() +method, as shown here:

    + +
    +TvContentRating rating = TvContentRating.createRating(
    +    "com.android.tv",
    +    "US_TV",
    +    "US_TV_PG",
    +    "US_TV_D", "US_TV_L");
    +
    + +

    Handle Track Selection

    + +

    The {@link android.media.tv.TvTrackInfo} class holds information about media tracks such +as the track type (video, audio, or subtitle) and so forth.

    + +

    The first time your TV input session is able to get track information, it should call +TvInputService.Session.notifyTracksChanged() with a list of all tracks to update the system TV app. When there +is a change in track information, call +notifyTracksChanged() +again to update the system. + +

    + +

    The system TV app provides an interface for the user to select a specific track if more than one +track is available for a given track type; for example, subtitles in different languages. Your TV +input responds to the +{@link android.media.tv.TvInputService.Session#onSelectTrack(int, java.lang.String) onSelectTrack()} +call from the system TV app by calling +{@link android.media.tv.TvInputService.Session#notifyTrackSelected(int, java.lang.String) notifyTrackSelected()} +, as shown in the following example. Note that when null +is passed as the track ID, this deselects the track.

    + +
    +@Override
    +public boolean onSelectTrack(int type, String trackId) {
    +    if (mPlayer != null) {
    +        if (type == TvTrackInfo.TYPE_SUBTITLE) {
    +            if (!mCaptionEnabled && trackId != null) {
    +                return false;
    +            }
    +            mSelectedSubtitleTrackId = trackId;
    +            if (trackId == null) {
    +                mSubtitleView.setVisibility(View.INVISIBLE);
    +            }
    +        }
    +        if (mPlayer.selectTrack(type, trackId)) {
    +            notifyTrackSelected(type, trackId);
    +            return true;
    +        }
    +    }
    +    return false;
    +}
    +
    + + + + + + +