Also replaced the selections from some database operations with the new buildXXX() helper methods since they are now not allowed. Change-Id: I5cf4e9d219c6437ab751b1a9148e1e7792cdfec2
180 lines
6.1 KiB
Java
180 lines
6.1 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.example.android.atsctvinput;
|
|
|
|
import android.content.ContentUris;
|
|
import android.content.ContentValues;
|
|
import android.content.res.AssetFileDescriptor;
|
|
import android.database.Cursor;
|
|
import android.media.MediaPlayer;
|
|
import android.media.tv.TvContract;
|
|
import android.media.tv.TvInputService;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.util.Log;
|
|
import android.util.Pair;
|
|
import android.view.Surface;
|
|
|
|
import com.example.android.atsctvinput.SampleTsStream.TsStream;
|
|
import com.example.android.atsctvinput.SectionParser.EITItem;
|
|
import com.example.android.atsctvinput.SectionParser.VCTItem;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* A sample TvInputService which plays ATSC TV stream.
|
|
*/
|
|
public class AtscTvInputService extends TvInputService {
|
|
private static final String TAG = "AtscTvInputService";
|
|
private static final int SEC_IN_MS = 1000;
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
Log.d(TAG, "onCreate()");
|
|
setAvailable(true);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
Log.d(TAG, "onDestroy()");
|
|
}
|
|
|
|
@Override
|
|
public TvInputSessionImpl onCreateSession() {
|
|
return new MyTvInputSessionImpl();
|
|
}
|
|
|
|
|
|
public TsStream getTsStreamForChannel(Uri channelUri) {
|
|
String[] projection = { TvContract.Channels.COLUMN_DATA };
|
|
if (channelUri == null) {
|
|
return null;
|
|
}
|
|
Cursor cursor = this.getContentResolver().query(
|
|
channelUri, projection, null, null, null);
|
|
if (cursor == null) {
|
|
return null;
|
|
}
|
|
if (cursor.getCount() < 1) {
|
|
cursor.close();
|
|
return null;
|
|
}
|
|
cursor.moveToNext();
|
|
TsStream stream = SampleTsStream.getTsStreamFromTuneInfo(cursor.getString(0));
|
|
cursor.close();
|
|
return stream;
|
|
}
|
|
|
|
private class MyTvInputSessionImpl extends TvInputSessionImpl {
|
|
private MediaPlayer mPlayer;
|
|
|
|
protected MyTvInputSessionImpl() {
|
|
mPlayer = new MediaPlayer();
|
|
}
|
|
|
|
@Override
|
|
public void onRelease() {
|
|
if (mPlayer != null) {
|
|
mPlayer.release();
|
|
mPlayer = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onSetSurface(Surface surface) {
|
|
Log.d(TAG, "onSetSurface(" + surface + ")");
|
|
mPlayer.setSurface(surface);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onSetVolume(float volume) {
|
|
Log.d(TAG, "onSetVolume(" + volume + ")");
|
|
mPlayer.setVolume(volume, volume);
|
|
}
|
|
|
|
@Override
|
|
public boolean onTune(Uri channelUri) {
|
|
Log.d(TAG, "onTune(" + channelUri + ")");
|
|
mPlayer.reset();
|
|
TsStream stream = getTsStreamForChannel(channelUri);
|
|
if (stream == null) {
|
|
return false;
|
|
}
|
|
new ProgramUpdateTask().execute(stream, channelUri);
|
|
AssetFileDescriptor afd = getResources().openRawResourceFd(stream.mResourceId);
|
|
try {
|
|
mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
|
|
afd.getLength());
|
|
mPlayer.prepare();
|
|
afd.close();
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "Failed to tune to(" + channelUri + ")");
|
|
mPlayer.reset();
|
|
return false;
|
|
}
|
|
mPlayer.start();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private class ProgramUpdateTask extends AsyncTask<Object, Void, Void> {
|
|
@Override
|
|
protected Void doInBackground(Object... objs) {
|
|
TsStream stream = (TsStream) objs[0];
|
|
Uri channelUri = (Uri) objs[1];
|
|
Pair<VCTItem, List<EITItem>> result =
|
|
SampleTsStream.extractChannelInfo(AtscTvInputService.this, stream);
|
|
if (result == null) {
|
|
return null;
|
|
}
|
|
clearPrograms(channelUri);
|
|
// The sample streams have passed timestamps. In order to show the metadata properly in
|
|
// TV app, we offset the time here.
|
|
long timeOffsetMs = Long.MIN_VALUE;
|
|
long currentTimeMs = System.currentTimeMillis();
|
|
for (EITItem i : result.second) {
|
|
if (timeOffsetMs == Long.MIN_VALUE) {
|
|
timeOffsetMs = currentTimeMs - i.getStartTime() * SEC_IN_MS;
|
|
}
|
|
insertProgram(channelUri, i, timeOffsetMs);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void clearPrograms(Uri channelUri) {
|
|
Uri uri = TvContract.buildProgramsUriForChannel(channelUri);
|
|
getContentResolver().delete(uri, null, null);
|
|
}
|
|
|
|
private Uri insertProgram(Uri channelUri, EITItem event, long timeOffsetMs) {
|
|
Log.d(TAG, "insertProgram " + event.getTitleText());
|
|
ContentValues values = new ContentValues();
|
|
values.put(TvContract.Programs.COLUMN_CHANNEL_ID, ContentUris.parseId(channelUri));
|
|
values.put(TvContract.Programs.COLUMN_TITLE, event.getTitleText());
|
|
values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, timeOffsetMs
|
|
+ event.getStartTime() * SEC_IN_MS);
|
|
values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, timeOffsetMs
|
|
+ (event.getStartTime() + event.getLengthInSecond()) * SEC_IN_MS);
|
|
return getContentResolver().insert(
|
|
TvContract.Programs.CONTENT_URI, values);
|
|
}
|
|
}
|
|
}
|