Developer Console

Step 4: Insert Programs

If you choose not to integrate with Gracenote, you must regularly insert the currently airing and upcoming program information for all of your channels into Android’s TV Database.

Construct and Insert Program Metadata

For quick demo purposes, you can hard-code some program metadata with the channel ID of the previously inserted channel, and insert the program data into the database. This code goes into SetupActivity.

Example of inserting program metadata:

import androidx.tvprovider.media.tv.Program;
import android.content.ContentProviderOperation;
import java.util.ArrayList;
import android.os.RemoteException;
import android.content.OperationApplicationException;

private void insertPrograms(long channelId) {
    ArrayList < ContentProviderOperation > ops = new ArrayList < > ();

    long oneHourMs = 1000 * 60 * 60;

    Program testProgram = new Program.Builder()
        .setChannelId(channelId)
        .setTitle("My Test Program")
        .setStartTimeUtcMillis(System.currentTimeMillis()) //just a sample time to see program displayed
        .setEndTimeUtcMillis(System.currentTimeMillis() + oneHourMs)
        .build();

    ops.add(ContentProviderOperation
        .newInsert(TvContract.Programs.CONTENT_URI)
        .withValues(testProgram.toContentValues())
        .build());
    try {
        getApplicationContext().getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
    } catch (RemoteException | OperationApplicationException e) {
        Log.e("SetupActivity", "Failed to insert programs.", e);
    }
}
private fun insertPrograms(channelId: Long) {
    val oneHourMs = (1000 * 60 * 60).toLong()
    val testProgram: Program = Program.Builder()
        .setChannelId(channelId)
        .setTitle("My Test Program")
        .setStartTimeUtcMillis(System.currentTimeMillis()) //just a sample time to see program displayed
        .setEndTimeUtcMillis(System.currentTimeMillis() + oneHourMs)
        .build()
    val ops = arrayListOf(
    ContentProviderOperation
        .newInsert(TvContract.Programs.CONTENT_URI)
        .withValues(testProgram.toContentValues())
        .build())
    try {
        contentResolver.applyBatch(TvContract.AUTHORITY, ops)
    } catch (e: RemoteException) {
        Log.e("SetupActivity", "Failed to insert programs.", e)
    } catch (e: OperationApplicationException) {
        Log.e("SetupActivity", "Failed to insert programs.", e)
    }
}

For our initial implementation we can then add the call to the insertPrograms method in the SetupActivity. Here is what the full method should look like:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.rich_setup);

    long channelId = insertChannel();
    insertPrograms(channelId);
}
public override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.rich_setup)
    val channelId = insertChannel()
    if (channelId != null) {
        insertPrograms(channelId)
    }
}

Notes:

  • Program metadata is inserted/updated/deleted/queried through ContentResolver the same way as a channel.
  • Here is the complete list of program metadata you can supply. Also, see Obtaining Programming Metadata.
  • We recommend you use the Program model provided by the AndroidX support library to construct the ContentValues.
  • Before insertion, you must assign a valid channel ID to the value. The channel ID should be a unique ID for the channel that the program belongs to.
  • Make sure there are no program gaps or overlapping program times in the data.

Checkpoint - Showing Program Metadata in the UI

  1. Build and install your APK onto Fire TV.
  2. Navigate to Settings > Live TV > Sync Sources and select your input.
  3. Navigate to the On Now row or Live Tab. The channels with programs currently airing should be displayed.
  4. Under the Live TV tab, navigate to the Channel Guide. You should see the test program you inserted playing on the test channel.

Troubleshooting

I don't see any program metadata displayed in the browse cards (content boxes seen when browsing)

If you are Gracenote integrated, refer to the troubleshooting section of "Step 3: Insert Your First Channel" to triage the Gracenote ID.

If you are not Gracenote integrated, please ensure that the start time and end time of the inserted programs are valid according to the current time.

"Program information Not available" will display as the default text when there is no program metadata for that period of time.

Verify the following in the logs:

  1. You have inserted program metadata for that period of time.
  2. The start time and end time of that program does not overlap with previous or next programs in the same channel. Fire TV's channel guide is designed to handle linear programs without any time overlap.

All channel rows after a certain row show "Program Information Not available"

This is usually caused by an incomplete program metadata insertion. It can be verified through the logs. Programs should not be inserted if they don’t have program info.

The most common reason for an incomplete metadata insertion is because of the timeout of Android's JobScheduler. Android has a timeout of 10 minutes (or 1 minute on Lollipop) for any ongoing jobs. It's not explicitly called out in JobScheduler's documentation, but is mentioned in the WorkManager's doc (which can use JobScheduler's underlying dependancies for Android's API level).

Typical workarounds include:

  • Insert less program data to avoid hitting the Android timeout. The recommended number of future programs per channel is on a case-by-case basis and should be included as part of feature discussions.
  • Insert program data in smaller chunks, using different sync jobs.
  • Implement a robust retry logic to handle the time-out case.

Next Steps

Go to the next step: Step 5: Playback in Fire TV UI.


Last updated: Aug 12, 2022