TunableTvView.java revision d82ca62a0fefaed41493540df7d622cfcfb2cb6c
1d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leepackage com.android.tv.ui; 2900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 3900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.content.Context; 4829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.content.pm.PackageManager; 59035590d94d136a020e499ce720b9d2cf1f6f45cJae Seoimport android.media.tv.TvInputInfo; 64e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kimimport android.media.tv.TvInputManager; 75459428da9aca1b91e3a976a80e1db783caac674Dongwon Kangimport android.media.tv.TvTrackInfo; 89035590d94d136a020e499ce720b9d2cf1f6f45cJae Seoimport android.media.tv.TvView; 9d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.media.tv.TvView.OnUnhandledInputEventListener; 10d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.media.tv.TvView.TvInputListener; 11cdde1b3be4391c4b0d3b57cce3c63eee8c81819aDongwon Kangimport android.net.Uri; 12900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.util.AttributeSet; 13900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.util.Log; 14829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.view.KeyEvent; 15829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.view.MotionEvent; 16900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.view.SurfaceHolder; 17b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kangimport android.view.SurfaceView; 18d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.view.ViewGroup; 19d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.widget.FrameLayout; 20900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 214e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kimimport com.android.tv.R; 22d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.data.Channel; 2321abb13274f379e69c3c86d51e493122cccca345Dongwon Kangimport com.android.tv.data.StreamInfo; 24d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.util.TvInputManagerHelper; 25d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.util.Utils; 26900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 275459428da9aca1b91e3a976a80e1db783caac674Dongwon Kangimport java.util.List; 285459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang 29d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenpublic class TunableTvView extends FrameLayout implements StreamInfo { 30d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten // STOPSHIP: Turn debugging off 31900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private static final boolean DEBUG = true; 32900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private static final String TAG = "TunableTvView"; 33900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 34900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private static final int DELAY_FOR_SURFACE_RELEASE = 300; 35829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho public static final String PERMISSION_RECEIVE_INPUT_EVENT = 36829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho "android.permission.RECEIVE_INPUT_EVENT"; 37900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 38d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private final TvView mTvView; 39d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private final SurfaceView mSurfaceView; 40900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private long mChannelId = Channel.INVALID_ID; 41900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private TvInputManagerHelper mInputManagerHelper; 42900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private boolean mStarted; 43900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private TvInputInfo mInputInfo; 44900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private OnTuneListener mOnTuneListener; 45e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim private int mVideoWidth; 46e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim private int mVideoHeight; 4721abb13274f379e69c3c86d51e493122cccca345Dongwon Kang private int mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN; 48e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang private int mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN; 49e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang private boolean mHasClosedCaption = false; 504e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim private boolean mIsVideoAvailable; 514e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim private int mVideoUnavailableReason; 52829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho private boolean mCanReceiveInputEvent; 53d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private boolean mIsMuted; 54d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private float mVolume; 55900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 56900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { 57900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho @Override 58900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } 59900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 60900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho @Override 61900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public void surfaceCreated(SurfaceHolder holder) { } 62900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 63900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho @Override 64900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public void surfaceDestroyed(SurfaceHolder holder) { 65900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho // TODO: It is a hack to wait to release a surface at TIS. If there is a way to 66900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho // know when the surface is released at TIS, we don't need this hack. 67900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho try { 68900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (DEBUG) Log.d(TAG, "Sleep to wait destroying a surface"); 69900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho Thread.sleep(DELAY_FOR_SURFACE_RELEASE); 70900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (DEBUG) Log.d(TAG, "Wake up from sleeping"); 71900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } catch (InterruptedException e) { 72900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho e.printStackTrace(); 73900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 74900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 75900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho }; 76900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 779bb548ba08d7e222f276287fead545c6a11704d8Jae Seo private final TvInputListener mListener = 789bb548ba08d7e222f276287fead545c6a11704d8Jae Seo new TvInputListener() { 79900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho @Override 809bb548ba08d7e222f276287fead545c6a11704d8Jae Seo public void onError(String inputId, int errorCode) { 819bb548ba08d7e222f276287fead545c6a11704d8Jae Seo if (errorCode == TvView.ERROR_BUSY) { 829bb548ba08d7e222f276287fead545c6a11704d8Jae Seo Log.w(TAG, "Failed to bind an input"); 83900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho long channelId = mChannelId; 84900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mChannelId = Channel.INVALID_ID; 85900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mInputInfo = null; 86829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho mCanReceiveInputEvent = false; 87900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (mOnTuneListener != null) { 88900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mOnTuneListener.onTuned(false, channelId); 89900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mOnTuneListener = null; 90900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 919bb548ba08d7e222f276287fead545c6a11704d8Jae Seo } else if (errorCode == TvView.ERROR_TV_INPUT_DISCONNECTED) { 929bb548ba08d7e222f276287fead545c6a11704d8Jae Seo Log.w(TAG, "Session is released by crash"); 939bb548ba08d7e222f276287fead545c6a11704d8Jae Seo long channelId = mChannelId; 949bb548ba08d7e222f276287fead545c6a11704d8Jae Seo mChannelId = Channel.INVALID_ID; 959bb548ba08d7e222f276287fead545c6a11704d8Jae Seo mInputInfo = null; 96829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho mCanReceiveInputEvent = false; 979bb548ba08d7e222f276287fead545c6a11704d8Jae Seo if (mOnTuneListener != null) { 989bb548ba08d7e222f276287fead545c6a11704d8Jae Seo mOnTuneListener.onUnexpectedStop(channelId); 999bb548ba08d7e222f276287fead545c6a11704d8Jae Seo mOnTuneListener = null; 1009bb548ba08d7e222f276287fead545c6a11704d8Jae Seo } 101900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 102900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 103900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 104900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho @Override 1055459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang public void onChannelRetuned(String inputId, Uri channelUri) { 1068f1ff2894363afbb6909a3a1edc1f3cbe8657c11Jae Seo if (DEBUG) { 1075459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang Log.d(TAG, "onChannelRetuned(inputId=" + inputId + ", channelUri=" 1085459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang + channelUri + ")"); 1098f1ff2894363afbb6909a3a1edc1f3cbe8657c11Jae Seo } 1105459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang // TODO: update {@code mChannelId}. 11121abb13274f379e69c3c86d51e493122cccca345Dongwon Kang if (mOnTuneListener != null) { 1125459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mOnTuneListener.onChannelChanged(channelUri); 11321abb13274f379e69c3c86d51e493122cccca345Dongwon Kang } 11421abb13274f379e69c3c86d51e493122cccca345Dongwon Kang } 115fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang 116fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang @Override 1175459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang public void onTrackInfoChanged(String inputId, List<TvTrackInfo> tracks) { 1185459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang for (TvTrackInfo track : tracks) { 1195459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang int type = track.getInt(TvTrackInfo.KEY_TYPE); 1205459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang boolean selected = track.getBoolean(TvTrackInfo.KEY_IS_SELECTED); 1215459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang if (type == TvTrackInfo.VALUE_TYPE_VIDEO && selected) { 1225459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mVideoWidth = track.getInt(TvTrackInfo.KEY_WIDTH); 1235459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mVideoHeight = track.getInt(TvTrackInfo.KEY_HEIGHT); 1245459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mVideoFormat = Utils.getVideoDefinitionLevelFromSize( 1255459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mVideoWidth, mVideoHeight); 1265459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang } else if (type == TvTrackInfo.VALUE_TYPE_AUDIO && selected) { 1275459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mAudioChannelCount = track.getInt(TvTrackInfo.KEY_CHANNEL_COUNT); 1285459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang } else if (type == TvTrackInfo.VALUE_TYPE_SUBTITLE) { 1295459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang mHasClosedCaption = true; 1305459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang } 131fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang } 132fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang if (mOnTuneListener != null) { 133fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang mOnTuneListener.onStreamInfoChanged(TunableTvView.this); 134fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang } 135fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang } 1364e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim 1374e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim @Override 1384e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim public void onVideoAvailable(String inputId) { 1394e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim mIsVideoAvailable = true; 140d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten unblock(); 1414e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim if (mOnTuneListener != null) { 1424e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim mOnTuneListener.onStreamInfoChanged(TunableTvView.this); 1434e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 1444e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 1454e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim 1464e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim @Override 1474e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim public void onVideoUnavailable(String inputId, int reason) { 1484e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim mIsVideoAvailable = false; 1494e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim mVideoUnavailableReason = reason; 150d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten block(reason); 1514e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim if (mOnTuneListener != null) { 1524e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim mOnTuneListener.onStreamInfoChanged(TunableTvView.this); 1534e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 1544e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 155900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho }; 156900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 157900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public TunableTvView(Context context) { 158d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten this(context, null); 159900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 160900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 161900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public TunableTvView(Context context, AttributeSet attrs) { 162900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho this(context, attrs, 0); 163900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 164900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 165900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public TunableTvView(Context context, AttributeSet attrs, int defStyleAttr) { 166d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten this(context, attrs, defStyleAttr, 0); 167d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 168d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 169d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten public TunableTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 170d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten super(context, attrs, defStyleAttr, defStyleRes); 171d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten inflate(getContext(), R.layout.tunable_tv_view, this); 172d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 173d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView = (TvView) findViewById(R.id.tv_view); 174d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mSurfaceView = findSurfaceView(mTvView); 175900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 176900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 177900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public void start(TvInputManagerHelper tvInputManagerHelper) { 178900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mInputManagerHelper = tvInputManagerHelper; 179900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (mStarted) { 180900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return; 181900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 182900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mStarted = true; 183900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 184900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 185900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public void stop() { 186900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (!mStarted) { 187900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return; 188900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 189900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mStarted = false; 190d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.reset(); 191900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mChannelId = Channel.INVALID_ID; 192900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mInputInfo = null; 193829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho mCanReceiveInputEvent = false; 194900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mOnTuneListener = null; 195900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 196900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 197900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public boolean isPlaying() { 198900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return mStarted; 199900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 200900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 201900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public boolean tuneTo(long channelId, OnTuneListener listener) { 202900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (!mStarted) { 203900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho throw new IllegalStateException("TvView isn't started"); 204900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 205900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (DEBUG) Log.d(TAG, "tuneTo " + channelId); 206e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim mVideoWidth = 0; 207e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim mVideoHeight = 0; 20821abb13274f379e69c3c86d51e493122cccca345Dongwon Kang mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN; 209e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN; 210e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang mHasClosedCaption = false; 211900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho String inputId = Utils.getInputIdForChannel(getContext(), channelId); 212900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho TvInputInfo inputInfo = mInputManagerHelper.getTvInputInfo(inputId); 213085333963f5b4cbfa9d6e42512690ffbca873b96Wonsik Kim if (inputInfo == null 2144e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim || mInputManagerHelper.getInputState(inputInfo) == 2154e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim TvInputManager.INPUT_STATE_DISCONNECTED) { 216900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return false; 217900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 218900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mOnTuneListener = listener; 219900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mChannelId = channelId; 220900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (!inputInfo.equals(mInputInfo)) { 221d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.reset(); 222900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho // TODO: It is a hack to wait to release a surface at TIS. If there is a way to 223900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho // know when the surface is released at TIS, we don't need this hack. 224900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho try { 225900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho Thread.sleep(DELAY_FOR_SURFACE_RELEASE); 226900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } catch (InterruptedException e) { 227900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho e.printStackTrace(); 228900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 229900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho mInputInfo = inputInfo; 230d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mCanReceiveInputEvent = getContext().getPackageManager().checkPermission( 231829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho PERMISSION_RECEIVE_INPUT_EVENT, mInputInfo.getComponent().getPackageName()) 232829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho == PackageManager.PERMISSION_GRANTED; 2339bb548ba08d7e222f276287fead545c6a11704d8Jae Seo } 234d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.setTvInputListener(mListener); 235d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.tune(mInputInfo.getId(), Utils.getChannelUri(mChannelId)); 2369bb548ba08d7e222f276287fead545c6a11704d8Jae Seo if (mOnTuneListener != null) { 2379bb548ba08d7e222f276287fead545c6a11704d8Jae Seo // TODO: Add a callback for tune complete and call onTuned when it was successful. 2389bb548ba08d7e222f276287fead545c6a11704d8Jae Seo mOnTuneListener.onTuned(true, mChannelId); 239900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 240900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return true; 241900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 242900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 24387c1cbb508c96538d1e69ba1fdf3d9f4d52710d6Jaewan Kim @Override 244900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public TvInputInfo getCurrentTvInputInfo() { 245900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return mInputInfo; 246900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 247900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 248900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public long getCurrentChannelId() { 249900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho return mChannelId; 250900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 251900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 252b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kang public void setPip(boolean isPip) { 253d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mSurfaceView.setZOrderMediaOverlay(isPip); 254b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kang } 255b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kang 256561696c364ee330f4316544f1f85498393883526Jae Seo public void setStreamVolume(float volume) { 257900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho if (!mStarted) { 258900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho throw new IllegalStateException("TvView isn't started"); 259900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 260d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten if (DEBUG) Log.d(TAG, "setStreamVolume " + volume); 261d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mVolume = volume; 262d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten if (!mIsMuted) { 263d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.setStreamVolume(volume); 264d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 265900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 266900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho 267829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho @Override 268829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho public boolean dispatchKeyEvent(KeyEvent event) { 269d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return mCanReceiveInputEvent && mTvView.dispatchKeyEvent(event); 270829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho } 271829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho 272829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho @Override 273829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho public boolean dispatchTouchEvent(MotionEvent event) { 274d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return mCanReceiveInputEvent && mTvView.dispatchTouchEvent(event); 275829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho } 276829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho 277829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho @Override 278829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho public boolean dispatchTrackballEvent(MotionEvent event) { 279d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return mCanReceiveInputEvent && mTvView.dispatchTrackballEvent(event); 280829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho } 281829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho 282829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho @Override 283829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho public boolean dispatchGenericMotionEvent(MotionEvent event) { 284d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return mCanReceiveInputEvent && mTvView.dispatchGenericMotionEvent(event); 285829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho } 286829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho 287900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho public interface OnTuneListener { 288900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho void onTuned(boolean success, long channelId); 289900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho void onUnexpectedStop(long channelId); 29021abb13274f379e69c3c86d51e493122cccca345Dongwon Kang void onStreamInfoChanged(StreamInfo info); 291cdde1b3be4391c4b0d3b57cce3c63eee8c81819aDongwon Kang void onChannelChanged(Uri channel); 29221abb13274f379e69c3c86d51e493122cccca345Dongwon Kang } 29321abb13274f379e69c3c86d51e493122cccca345Dongwon Kang 29421abb13274f379e69c3c86d51e493122cccca345Dongwon Kang @Override 295e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim public int getVideoWidth() { 296e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim return mVideoWidth; 297e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim } 298e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim 299e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim @Override 300e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim public int getVideoHeight() { 301e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim return mVideoHeight; 302e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim } 303e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim 304e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim @Override 30521abb13274f379e69c3c86d51e493122cccca345Dongwon Kang public int getVideoDefinitionLevel() { 30621abb13274f379e69c3c86d51e493122cccca345Dongwon Kang return mVideoFormat; 307900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho } 308e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang 309e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang @Override 310e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang public int getAudioChannelCount() { 311e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang return mAudioChannelCount; 312e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang } 313e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang 314e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang @Override 315e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang public boolean hasClosedCaption() { 316e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang return mHasClosedCaption; 317e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang } 3184e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim 3194e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim @Override 3204e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim public boolean isVideoAvailable() { 3214e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim return mIsVideoAvailable; 3224e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 3234e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim 3244e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim @Override 3254e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim public int getVideoUnavailableReason() { 3264e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim return mVideoUnavailableReason; 3274e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim } 328d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 329d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) { 330d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.setOnUnhandledInputEventListener(listener); 331d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 332d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 333d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten public List<TvTrackInfo> getTracks() { 334d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return mTvView.getTracks(); 335d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 336d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 337d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten public void selectTrack(TvTrackInfo track) { 338d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.selectTrack(track); 339d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 340d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 341d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private void block(int reason) { 342d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten hideBlock(); 343d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten switch (reason) { 344d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN: 345d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten default: 346d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_unknown).setVisibility(VISIBLE); 347d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten break; 348d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNE: 349d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_tune).setVisibility(VISIBLE); 350d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten break; 351d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL: 352d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_weak_signal).setVisibility(VISIBLE); 353d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten break; 354d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING: 355d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_buffering).setVisibility(VISIBLE); 356d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten break; 357d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 358d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mute(); 359d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 360d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 361d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private void unblock() { 362d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten hideBlock(); 363d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten unmute(); 364d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 365d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 366d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private void mute() { 367d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mIsMuted = true; 368d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.setStreamVolume(0); 369d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 370d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 371d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private void unmute() { 372d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mIsMuted = false; 373d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten mTvView.setStreamVolume(mVolume); 374d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 375d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 376d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private SurfaceView findSurfaceView(ViewGroup view) { 377d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten for (int i = 0; i < view.getChildCount(); ++i) { 378d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten if (view.getChildAt(i) instanceof SurfaceView) { 379d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten SurfaceView surfaceView = (SurfaceView) mTvView.getChildAt(i); 380d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten surfaceView.getHolder().addCallback(mSurfaceHolderCallback); 381d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten return surfaceView; 382d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 383d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 384d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten throw new RuntimeException("TvView does not have SurfaceView."); 385d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 386d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten 387d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten private void hideBlock() { 388d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_unknown).setVisibility(GONE); 389d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_tune).setVisibility(GONE); 390d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_weak_signal).setVisibility(GONE); 391d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten findViewById(R.id.block_reason_buffering).setVisibility(GONE); 392d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten } 393900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho} 394