1 /* 2 * Copyright 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.sampletvinput.simple; 18 19 import android.content.Context; 20 import android.content.res.AssetFileDescriptor; 21 import android.database.Cursor; 22 import android.media.MediaPlayer; 23 import android.media.tv.TvContract; 24 import android.media.tv.TvInputManager; 25 import android.media.tv.TvInputService; 26 import android.net.Uri; 27 import android.view.Surface; 28 29 import com.example.android.sampletvinput.R; 30 31 import java.io.IOException; 32 33 /** 34 * Simple TV input service which provides two sample channels. 35 * <p> 36 * NOTE: The purpose of this sample is to provide a really simple TV input sample to the developers 37 * so that they can understand the core APIs and when/how/where they should use them with ease. 38 * This means lots of features including EPG, subtitles, multi-audio, parental controls, and overlay 39 * view are missing here. So, to check the example codes for them, see {@link RichTvInputService}. 40 * </p> 41 */ 42 public class SimpleTvInputService extends TvInputService { 43 @Override 44 public Session onCreateSession(String inputId) { 45 return new SimpleSessionImpl(this); 46 } 47 48 /** 49 * Simple session implementation which plays local videos on the application's tune request. 50 */ 51 private class SimpleSessionImpl extends TvInputService.Session { 52 private static final int RESOURCE_1 = 53 R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_22050hz; 54 private static final int RESOURCE_2 = 55 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz; 56 57 private MediaPlayer mPlayer; 58 private float mVolume; 59 private Surface mSurface; 60 61 SimpleSessionImpl(Context context) { 62 super(context); 63 } 64 65 @Override 66 public void onRelease() { 67 mPlayer.release(); 68 } 69 70 @Override 71 public boolean onSetSurface(Surface surface) { 72 if (mPlayer != null) { 73 mPlayer.setSurface(surface); 74 } 75 mSurface = surface; 76 return true; 77 } 78 79 @Override 80 public void onSetStreamVolume(float volume) { 81 if (mPlayer != null) { 82 mPlayer.setVolume(volume, volume); 83 } 84 mVolume = volume; 85 } 86 87 @Override 88 public boolean onTune(Uri channelUri) { 89 String[] projection = {TvContract.Channels.COLUMN_SERVICE_ID}; 90 int resource = RESOURCE_1; 91 Cursor cursor = null; 92 try { 93 cursor = getContentResolver().query(channelUri, projection, null, null, null); 94 if (cursor == null || cursor.getCount() == 0) { 95 return false; 96 } 97 cursor.moveToNext(); 98 resource = (cursor.getInt(0) == SimpleTvInputSetupActivity.CHANNEL_1_SERVICE_ID ? 99 RESOURCE_1 : RESOURCE_2); 100 } finally { 101 if (cursor != null) { 102 cursor.close(); 103 } 104 } 105 return startPlayback(resource); 106 // NOTE: To display the program information (e.g. title) properly in the channel banner, 107 // The implementation needs to register the program metadata on TvProvider. 108 // For the example implementation, please see {@link RichTvInputService}. 109 } 110 111 @Override 112 public void onSetCaptionEnabled(boolean enabled) { 113 // The sample content does not have caption. Nothing to do in this sample input. 114 // NOTE: If the channel has caption, the implementation should turn on/off the caption 115 // based on {@code enabled}. 116 // For the example implementation for the case, please see {@link RichTvInputService}. 117 } 118 119 private boolean startPlayback(int resource) { 120 if (mPlayer == null) { 121 mPlayer = new MediaPlayer(); 122 mPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { 123 @Override 124 public boolean onInfo(MediaPlayer player, int what, int arg) { 125 // NOTE: TV input should notify the video playback state by using 126 // {@code notifyVideoAvailable()} and {@code notifyVideoUnavailable() so 127 // that the application can display back screen or spinner properly. 128 if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { 129 notifyVideoUnavailable( 130 TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING); 131 return true; 132 } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END 133 || what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 134 notifyVideoAvailable(); 135 return true; 136 } 137 return false; 138 } 139 }); 140 mPlayer.setSurface(mSurface); 141 mPlayer.setVolume(mVolume, mVolume); 142 } else { 143 mPlayer.reset(); 144 } 145 mPlayer.setLooping(true); 146 AssetFileDescriptor afd = getResources().openRawResourceFd(resource); 147 if (afd == null) { 148 return false; 149 } 150 try { 151 mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 152 afd.getDeclaredLength()); 153 mPlayer.prepare(); 154 mPlayer.start(); 155 } catch (IOException e) { 156 return false; 157 } finally { 158 try { 159 afd.close(); 160 } catch (IOException e) { 161 // Do nothing. 162 } 163 } 164 // The sample content does not have rating information. Just allow the content here. 165 // NOTE: If the content might include problematic scenes, it should not be allowed. 166 // Also, if the content has rating information, the implementation should allow the 167 // content based on the current rating settings by using 168 // {@link android.media.tv.TvInputManager#isRatingBlocked()}. 169 // For the example implementation for the case, please see {@link RichTvInputService}. 170 notifyContentAllowed(); 171 return true; 172 } 173 } 174 } 175