1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/*
2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License.
6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at
7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software
11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and
14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License.
15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
17d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leepackage com.android.tv.ui;
18900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.Animator;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.AnimatorListenerAdapter;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.animation.TimeInterpolator;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.annotation.SuppressLint;
23ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.annotation.TargetApi;
2448dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.content.ContentUris;
25900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.content.Context;
26829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.content.pm.PackageManager;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.PlaybackParams;
28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContentRating;
299035590d94d136a020e499ce720b9d2cf1f6f45cJae Seoimport android.media.tv.TvInputInfo;
304e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kimimport android.media.tv.TvInputManager;
315459428da9aca1b91e3a976a80e1db783caac674Dongwon Kangimport android.media.tv.TvTrackInfo;
3248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.tv.TvView;
33d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.media.tv.TvView.OnUnhandledInputEventListener;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvView.TvInputCallback;
3548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.net.ConnectivityManager;
36cdde1b3be4391c4b0d3b57cce3c63eee8c81819aDongwon Kangimport android.net.Uri;
3748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.os.AsyncTask;
38ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Build;
392c62d08f28cddda68005660c5cf62c5874b7f587Sungsoo Limimport android.os.Bundle;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.IntDef;
417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.support.annotation.Nullable;
4248dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.support.annotation.UiThread;
4348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.support.v4.os.BuildCompat;
44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.text.TextUtils;
45900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.util.AttributeSet;
46900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Choimport android.util.Log;
47829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.view.KeyEvent;
48829ca34b57d208a84430b2ccecd8975598d086daYoungsang Choimport android.view.MotionEvent;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.SurfaceView;
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.view.View;
51d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.view.ViewGroup;
52d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenimport android.widget.FrameLayout;
537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.widget.ImageView;
54900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.R;
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.TvApplication;
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.DurationTimer;
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
6048dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.feature.CommonFeatures;
6148dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.recording.RecordedProgram;
62d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.data.Channel;
6348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.data.ChannelDataManager;
6421abb13274f379e69c3c86d51e493122cccca345Dongwon Kangimport com.android.tv.data.StreamInfo;
657d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.data.WatchedHistoryManager;
6648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.dvr.DvrDataManager;
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.parental.ContentRatingsManager;
6807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport com.android.tv.recommendation.NotificationService;
6948dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.util.NetworkUtils;
707d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.PermissionUtils;
71d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.util.TvInputManagerHelper;
72d422754c0cc8475d6e2c8c079ed2ee2e96213edbChulwoo Leeimport com.android.tv.util.Utils;
73900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.lang.annotation.Retention;
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.lang.annotation.RetentionPolicy;
7648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.lang.reflect.InvocationTargetException;
7748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport java.lang.reflect.Method;
785459428da9aca1b91e3a976a80e1db783caac674Dongwon Kangimport java.util.List;
795459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang
80d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkerstenpublic class TunableTvView extends FrameLayout implements StreamInfo {
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final boolean DEBUG = false;
82900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    private static final String TAG = "TunableTvView";
83900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final int VIDEO_UNAVAILABLE_REASON_NOT_TUNED = -1;
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
8648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Retention(RetentionPolicy.SOURCE)
8748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @IntDef({BLOCK_SCREEN_TYPE_NO_UI, BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW, BLOCK_SCREEN_TYPE_NORMAL})
8848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public @interface BlockScreenType {}
89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final int BLOCK_SCREEN_TYPE_NO_UI = 0;
90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final int BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW = 1;
91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static final int BLOCK_SCREEN_TYPE_NORMAL = 2;
92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
9348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final String PERMISSION_RECEIVE_INPUT_EVENT =
9448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            "com.android.tv.permission.RECEIVE_INPUT_EVENT";
9548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Retention(RetentionPolicy.SOURCE)
97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @IntDef({ TIME_SHIFT_STATE_NONE, TIME_SHIFT_STATE_PLAY, TIME_SHIFT_STATE_PAUSE,
9848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            TIME_SHIFT_STATE_REWIND, TIME_SHIFT_STATE_FAST_FORWARD })
9948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private @interface TimeShiftState {}
10048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final int TIME_SHIFT_STATE_NONE = 0;
10148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final int TIME_SHIFT_STATE_PLAY = 1;
10248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final int TIME_SHIFT_STATE_PAUSE = 2;
10348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final int TIME_SHIFT_STATE_REWIND = 3;
10448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final int TIME_SHIFT_STATE_FAST_FORWARD = 4;
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int FADED_IN = 0;
107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int FADED_OUT = 1;
108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int FADING_IN = 2;
109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final int FADING_OUT = 3;
110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long INVALID_TIME = -1;
112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // It is too small to see the description text without PIP_BLOCK_SCREEN_SCALE_FACTOR.
114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final float PIP_BLOCK_SCREEN_SCALE_FACTOR = 1.2f;
115900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private AppLayerTvView mTvView;
117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Channel mCurrentChannel;
11848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private RecordedProgram mRecordedProgram;
119900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    private TvInputManagerHelper mInputManagerHelper;
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private ContentRatingsManager mContentRatingsManager;
1217d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    @Nullable
1227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private WatchedHistoryManager mWatchedHistoryManager;
123900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    private boolean mStarted;
124900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    private TvInputInfo mInputInfo;
125900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    private OnTuneListener mOnTuneListener;
126e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    private int mVideoWidth;
127e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    private int mVideoHeight;
12821abb13274f379e69c3c86d51e493122cccca345Dongwon Kang    private int mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private float mVideoFrameRate;
13048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private float mVideoDisplayAspectRatio;
131e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    private int mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
132e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    private boolean mHasClosedCaption = false;
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mVideoAvailable;
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mScreenBlocked;
135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private OnScreenBlockingChangedListener mOnScreenBlockedListener;
136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private TvContentRating mBlockedContentRating;
137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mVideoUnavailableReason = VIDEO_UNAVAILABLE_REASON_NOT_TUNED;
138829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    private boolean mCanReceiveInputEvent;
139d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    private boolean mIsMuted;
140d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    private float mVolume;
141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mParentControlEnabled;
142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mFixedSurfaceWidth;
143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mFixedSurfaceHeight;
144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mIsPip;
145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mScreenHeight;
146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mShrunkenTvViewHeight;
14748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final boolean mCanModifyParentalControls;
148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @TimeShiftState private int mTimeShiftState = TIME_SHIFT_STATE_NONE;
150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private TimeShiftListener mTimeShiftListener;
151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean mTimeShiftAvailable;
152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mTimeShiftCurrentPositionMs = INVALID_TIME;
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Tracker mTracker;
155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final DurationTimer mChannelViewTimer = new DurationTimer();
15648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private InternetCheckTask mInternetCheckTask;
157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // A block screen view which has lock icon with black background.
159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // This indicates that user's action is needed to play video.
16048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final BlockScreenView mBlockScreenView;
161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // A View to hide screen when there's problem in video playback.
16348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final BlockScreenView mHideScreenView;
164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // A View to block screen until onContentAllowed is received if parental control is on.
166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mBlockScreenForTuneView;
167900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // A spinner view to show buffering status.
169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mBufferingSpinnerView;
170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // A View for fade-in/out animation
172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final View mDimScreenView;
173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mFadeState = FADED_IN;
174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private Runnable mActionAfterFade;
175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
17648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @BlockScreenType private int mBlockScreenType;
17748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
17848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final DvrDataManager mDvrDataManager;
17948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final ChannelDataManager mChannelDataManager;
18048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final ConnectivityManager mConnectivityManager;
181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final TvInputCallback mCallback =
183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            new TvInputCallback() {
184900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho                @Override
185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onConnectionFailed(String inputId) {
186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    Log.w(TAG, "Failed to bind an input");
1877d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    mTracker.sendInputConnectionFailure(inputId);
188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    Channel channel = mCurrentChannel;
189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mCurrentChannel = null;
190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mInputInfo = null;
191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mCanReceiveInputEvent = false;
192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mOnTuneListener != null) {
193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        // If tune is called inside onTuneFailed, mOnTuneListener will be set to
194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        // a new instance. In order to avoid to clear the new mOnTuneListener,
195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        // we copy mOnTuneListener to l and clear mOnTuneListener before
196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        // calling onTuneFailed.
197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        OnTuneListener listener = mOnTuneListener;
198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mOnTuneListener = null;
199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        listener.onTuneFailed(channel);
200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onDisconnected(String inputId) {
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    Log.w(TAG, "Session is released by crash");
2067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    mTracker.sendInputDisconnected(inputId);
207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    Channel channel = mCurrentChannel;
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mCurrentChannel = null;
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mInputInfo = null;
210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mCanReceiveInputEvent = false;
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mOnTuneListener != null) {
212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        OnTuneListener listener = mOnTuneListener;
213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mOnTuneListener = null;
214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        listener.onUnexpectedStop(channel);
215900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho                    }
216900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho                }
217900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
218900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho                @Override
2195459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang                public void onChannelRetuned(String inputId, Uri channelUri) {
2208f1ff2894363afbb6909a3a1edc1f3cbe8657c11Jae Seo                    if (DEBUG) {
2215459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang                        Log.d(TAG, "onChannelRetuned(inputId=" + inputId + ", channelUri="
2225459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang                                + channelUri + ")");
2238f1ff2894363afbb6909a3a1edc1f3cbe8657c11Jae Seo                    }
22421abb13274f379e69c3c86d51e493122cccca345Dongwon Kang                    if (mOnTuneListener != null) {
225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mOnTuneListener.onChannelRetuned(channelUri);
22621abb13274f379e69c3c86d51e493122cccca345Dongwon Kang                    }
22721abb13274f379e69c3c86d51e493122cccca345Dongwon Kang                }
228fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang
229fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                @Override
230ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
231ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                    mHasClosedCaption = false;
232ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                    for (TvTrackInfo track : tracks) {
233ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) {
234ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            mHasClosedCaption = true;
235ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            break;
236ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        }
237ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                    }
238512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                    if (mOnTuneListener != null) {
239512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                        mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
240512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                    }
241512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                }
242512017945a2cd192797d7ca404cba08d792877ddDongwon Kang
243512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                @Override
244ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                public void onTrackSelected(String inputId, int type, String trackId) {
245ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                    if (trackId == null) {
246ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        // A track is unselected.
247512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                        if (type == TvTrackInfo.TYPE_VIDEO) {
248ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            mVideoWidth = 0;
249ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            mVideoHeight = 0;
250ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                            mVideoFrameRate = 0f;
25248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            mVideoDisplayAspectRatio = 0f;
253512017945a2cd192797d7ca404cba08d792877ddDongwon Kang                        } else if (type == TvTrackInfo.TYPE_AUDIO) {
254ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
255ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        }
256ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                    } else {
257ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        List<TvTrackInfo> tracks = getTracks(type);
258ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        boolean trackFound = false;
259ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        if (tracks != null) {
26007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                            for (TvTrackInfo track : tracks) {
261ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                if (track.getId().equals(trackId)) {
262ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                    if (type == TvTrackInfo.TYPE_VIDEO) {
263ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                        mVideoWidth = track.getVideoWidth();
264ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                        mVideoHeight = track.getVideoHeight();
265ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                        mVideoFormat = Utils.getVideoDefinitionLevelFromSize(
266ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                                mVideoWidth, mVideoHeight);
267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                                        mVideoFrameRate = track.getVideoFrameRate();
26848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                        if (mVideoWidth <= 0 || mVideoHeight <= 0) {
26948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                            mVideoDisplayAspectRatio = 0.0f;
27048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                        } else if (android.os.Build.VERSION.SDK_INT >=
27148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                                android.os.Build.VERSION_CODES.M) {
27248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                            float VideoPixelAspectRatio =
27348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                                    track.getVideoPixelAspectRatio();
27448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                            mVideoDisplayAspectRatio = VideoPixelAspectRatio
27548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                                    * mVideoWidth / mVideoHeight;
27648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                        } else {
27748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                            mVideoDisplayAspectRatio = mVideoWidth
27848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                                    / (float) mVideoHeight;
27948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                        }
280ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                    } else if (type == TvTrackInfo.TYPE_AUDIO) {
281ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                        mAudioChannelCount = track.getAudioChannelCount();
282ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                    }
283ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                    trackFound = true;
284ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                    break;
285ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                                }
286ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            }
287ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        }
288ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                        if (!trackFound) {
289ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang                            Log.w(TAG, "Invalid track ID: " + trackId);
2905459428da9aca1b91e3a976a80e1db783caac674Dongwon Kang                        }
291fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                    }
292fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                    if (mOnTuneListener != null) {
293fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                        mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
294fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                    }
295fc4ae3d84120d40af0d1ce5da7b0b1e40003e9aaDongwon Kang                }
2964e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim
2974e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                @Override
2984e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                public void onVideoAvailable(String inputId) {
299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    unhideScreenByVideoAvailability();
3004e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                    if (mOnTuneListener != null) {
3014e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                        mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
3024e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                    }
3034e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                }
3044e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim
3054e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                @Override
3064e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                public void onVideoUnavailable(String inputId, int reason) {
307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    hideScreenByVideoAvailability(reason);
3084e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                    if (mOnTuneListener != null) {
3094e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                        mOnTuneListener.onStreamInfoChanged(TunableTvView.this);
3104e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                    }
3117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    switch (reason) {
3127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
3137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
3147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
3157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                            mTracker.sendChannelVideoUnavailable(mCurrentChannel, reason);
3167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        default:
3177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                            // do nothing
3187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
3194e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim                }
320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onContentAllowed(String inputId) {
323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mBlockScreenForTuneView.setVisibility(View.GONE);
324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    unblockScreenByContentRating();
325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mOnTuneListener != null) {
326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mOnTuneListener.onContentAllowed();
327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onContentBlocked(String inputId, TvContentRating rating) {
332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    blockScreenByContentRating(rating);
333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mOnTuneListener != null) {
334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mOnTuneListener.onContentBlocked();
335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
339ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                @TargetApi(Build.VERSION_CODES.M)
340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onTimeShiftStatusChanged(String inputId, int status) {
34148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    boolean available = status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
34248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    setTimeShiftAvailable(available);
343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
344900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            };
345900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
346900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public TunableTvView(Context context) {
347d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        this(context, null);
348900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
349900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
350900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public TunableTvView(Context context, AttributeSet attrs) {
351900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        this(context, attrs, 0);
352900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
353900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
354900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public TunableTvView(Context context, AttributeSet attrs, int defStyleAttr) {
355d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        this(context, attrs, defStyleAttr, 0);
356d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
357d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
358d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    public TunableTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
359d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        super(context, attrs, defStyleAttr, defStyleRes);
360d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        inflate(getContext(), R.layout.tunable_tv_view, this);
361d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
3621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
36348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mDvrDataManager = CommonFeatures.DVR.isEnabled(context) && BuildCompat.isAtLeastN()
36448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                ? appSingletons.getDvrDataManager()
36548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                : null;
36648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mChannelDataManager = appSingletons.getChannelDataManager();
36748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mConnectivityManager = (ConnectivityManager) context
36848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .getSystemService(Context.CONNECTIVITY_SERVICE);
3697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mCanModifyParentalControls = PermissionUtils.hasModifyParentalControls(context);
3701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mTracker = appSingletons.getTracker();
371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBlockScreenType = BLOCK_SCREEN_TYPE_NORMAL;
37248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView = (BlockScreenView) findViewById(R.id.block_screen);
3737d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!mCanModifyParentalControls) {
37448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenView.setImage(R.drawable.ic_message_lock_no_permission);
37548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenView.setScaleType(ImageView.ScaleType.CENTER);
37648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        } else {
37748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenView.setImage(R.drawable.ic_message_lock);
3787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
37948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView.setShrunkenImage(R.drawable.ic_message_lock_preview);
38048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView.addFadeOutAnimationListener(new AnimatorListenerAdapter() {
381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animation) {
38348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                adjustBlockScreenSpacingAndText();
384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
38748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mHideScreenView = (BlockScreenView) findViewById(R.id.hide_screen);
38848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mHideScreenView.setImageVisibility(false);
389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBufferingSpinnerView = findViewById(R.id.buffering_spinner);
390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBlockScreenForTuneView = findViewById(R.id.block_screen_for_tune);
391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView = findViewById(R.id.dim);
392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.animate().setListener(new AnimatorListenerAdapter() {
393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationEnd(Animator animation) {
395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (mActionAfterFade != null) {
396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mActionAfterFade.run();
397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            @Override
401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            public void onAnimationCancel(Animator animation) {
402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (mActionAfterFade != null) {
403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mActionAfterFade.run();
404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        });
407816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void initialize(AppLayerTvView tvView, boolean isPip, int screenHeight,
410816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int shrunkenTvViewHeight) {
411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView = tvView;
412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mIsPip = isPip;
413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mScreenHeight = screenHeight;
414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mShrunkenTvViewHeight = shrunkenTvViewHeight;
415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.setZOrderOnTop(isPip);
416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        copyLayoutParamsToTvView();
417900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
418900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
419900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public void start(TvInputManagerHelper tvInputManagerHelper) {
420900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mInputManagerHelper = tvInputManagerHelper;
421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContentRatingsManager = tvInputManagerHelper.getContentRatingsManager();
422900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        if (mStarted) {
423900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            return;
424900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
425900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mStarted = true;
426900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
427900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
428900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public void stop() {
429900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        if (!mStarted) {
430900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            return;
431900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
432900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mStarted = false;
433816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel != null) {
4347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            long duration = mChannelViewTimer.reset();
4357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mTracker.sendChannelViewStop(mCurrentChannel, duration);
4367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (mWatchedHistoryManager != null && !mCurrentChannel.isPassthrough()) {
4377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mWatchedHistoryManager.logChannelViewStop(mCurrentChannel,
4387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        System.currentTimeMillis(), duration);
4397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
440816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
441816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        reset();
442816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
443816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
444816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void reset() {
445d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mTvView.reset();
446816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = null;
44748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecordedProgram = null;
448900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mInputInfo = null;
449829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho        mCanReceiveInputEvent = false;
450900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mOnTuneListener = null;
451816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setTimeShiftAvailable(false);
452816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        hideScreenByVideoAvailability(VIDEO_UNAVAILABLE_REASON_NOT_TUNED);
453816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
454816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
455816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setMain() {
456816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.setMain();
457900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
458900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
4597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public void setWatchedHistoryManager(WatchedHistoryManager watchedHistoryManager) {
4607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mWatchedHistoryManager = watchedHistoryManager;
4617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
4627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
463900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public boolean isPlaying() {
464900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        return mStarted;
465900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
466900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
467816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
468816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Called when parental control is changed.
469816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void onParentalControlChanged(boolean enabled) {
471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mParentControlEnabled = enabled;
472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mParentControlEnabled) {
473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mBlockScreenForTuneView.setVisibility(View.GONE);
474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
475816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
476816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
47848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Returns {@code true}, if this view is the recording playback mode.
47948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
48048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public boolean isRecordingPlayback() {
48148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mRecordedProgram != null;
48248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
48348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
48448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
48548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Returns the recording which is being played right now.
48648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
48748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public RecordedProgram getPlayingRecordedProgram() {
48848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mRecordedProgram;
48948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
49048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
49148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
492ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * Plays a recording.
493ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
49448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public boolean playRecording(Uri recordingUri, OnTuneListener listener) {
49548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!mStarted) {
49648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            throw new IllegalStateException("TvView isn't started");
49748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
49848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!CommonFeatures.DVR.isEnabled(getContext()) || !BuildCompat.isAtLeastN()) {
49948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return false;
50048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
50148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (DEBUG) Log.d(TAG, "playRecording " + recordingUri);
50248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        long recordingId = ContentUris.parseId(recordingUri);
50348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecordedProgram = mDvrDataManager.getRecordedProgram(recordingId);
50448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mRecordedProgram == null) {
50548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            Log.w(TAG, "No recorded program (Uri=" + recordingUri + ")");
50648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return false;
50748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
50848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        String inputId = mRecordedProgram.getInputId();
50948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        TvInputInfo inputInfo = mInputManagerHelper.getTvInputInfo(inputId);
51048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (inputInfo == null) {
51148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return false;
51248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
51348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mOnTuneListener = listener;
51448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // mCurrentChannel can be null.
51548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCurrentChannel = mChannelDataManager.getChannel(mRecordedProgram.getChannelId());
51648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // For recording playback, input event should not be sent.
51748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mCanReceiveInputEvent = false;
51848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        boolean needSurfaceSizeUpdate = false;
51948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!inputInfo.equals(mInputInfo)) {
52048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mInputInfo = inputInfo;
52148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (DEBUG) {
52248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                Log.d(TAG, "Input \'" + mInputInfo.getId() + "\' can receive input event: "
52348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        + mCanReceiveInputEvent);
52448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
52548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            needSurfaceSizeUpdate = true;
52648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
52748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mChannelViewTimer.start();
52848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoWidth = 0;
52948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoHeight = 0;
53048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
53148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoFrameRate = 0f;
53248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoDisplayAspectRatio = 0f;
53348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
53448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mHasClosedCaption = false;
53548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mTvView.setCallback(mCallback);
53648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mTimeShiftCurrentPositionMs = INVALID_TIME;
53748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mTvView.setTimeShiftPositionCallback(null);
53848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        setTimeShiftAvailable(false);
53948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mTvView.timeShiftPlay(inputId, recordingUri);
54048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (needSurfaceSizeUpdate && mFixedSurfaceWidth > 0 && mFixedSurfaceHeight > 0) {
54148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            // When the input is changed, TvView recreates its SurfaceView internally.
54248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            // So we need to call SurfaceHolder.setFixedSize for the new SurfaceView.
54348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            getSurfaceView().getHolder().setFixedSize(mFixedSurfaceWidth, mFixedSurfaceHeight);
54448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
54548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        hideScreenByVideoAvailability(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
54648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        unblockScreenByContentRating();
54748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mParentControlEnabled) {
54848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenForTuneView.setVisibility(View.VISIBLE);
54948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
55048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mOnTuneListener != null) {
55148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mOnTuneListener.onStreamInfoChanged(this);
55248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
55348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return true;
554ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
555ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
556ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
557816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Tunes to a channel with the {@code channelId}.
558816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
559816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param params extra data to send it to TIS and store the data in TIMS.
560816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @return false, if the TV input is not a proper state to tune to a channel. For example,
561816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *         if the state is disconnected or channelId doesn't exist, it returns false.
562816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
563816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean tuneTo(Channel channel, Bundle params, OnTuneListener listener) {
564900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        if (!mStarted) {
565900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            throw new IllegalStateException("TvView isn't started");
566900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
567816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) Log.d(TAG, "tuneTo " + channel);
568816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        TvInputInfo inputInfo = mInputManagerHelper.getTvInputInfo(channel.getInputId());
569816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (inputInfo == null) {
570900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            return false;
571900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
572816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mCurrentChannel != null) {
5737d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            long duration = mChannelViewTimer.reset();
5747d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mTracker.sendChannelViewStop(mCurrentChannel, duration);
5757d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (mWatchedHistoryManager != null && !mCurrentChannel.isPassthrough()) {
5767d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mWatchedHistoryManager.logChannelViewStop(mCurrentChannel,
5777d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        System.currentTimeMillis(), duration);
5787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
580900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        mOnTuneListener = listener;
581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = channel;
58248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecordedProgram = null;
58307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        boolean tunedByRecommendation = params != null
58407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                && params.getString(NotificationService.TUNE_PARAMS_RECOMMENDATION_TYPE) != null;
585816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        boolean needSurfaceSizeUpdate = false;
586900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        if (!inputInfo.equals(mInputInfo)) {
587900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            mInputInfo = inputInfo;
588d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten            mCanReceiveInputEvent = getContext().getPackageManager().checkPermission(
589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    PERMISSION_RECEIVE_INPUT_EVENT, mInputInfo.getServiceInfo().packageName)
590829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho                            == PackageManager.PERMISSION_GRANTED;
591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (DEBUG) {
592816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                Log.d(TAG, "Input \'" + mInputInfo.getId() + "\' can receive input event: "
593816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        + mCanReceiveInputEvent);
594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            needSurfaceSizeUpdate = true;
596816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
5977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mTracker.sendChannelViewStart(mCurrentChannel, tunedByRecommendation);
5987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mChannelViewTimer.start();
599816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVideoWidth = 0;
600816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVideoHeight = 0;
601816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVideoFormat = StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN;
602816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVideoFrameRate = 0f;
60348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoDisplayAspectRatio = 0f;
604816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mAudioChannelCount = StreamInfo.AUDIO_CHANNEL_COUNT_UNKNOWN;
605816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHasClosedCaption = false;
606816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.setCallback(mCallback);
607816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimeShiftCurrentPositionMs = INVALID_TIME;
608ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
609816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // To reduce the IPCs, unregister the callback here and register it when necessary.
610816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTvView.setTimeShiftPositionCallback(null);
611816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
612816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        setTimeShiftAvailable(false);
613816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.tune(mInputInfo.getId(), mCurrentChannel.getUri(), params);
614816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (needSurfaceSizeUpdate && mFixedSurfaceWidth > 0 && mFixedSurfaceHeight > 0) {
615816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // When the input is changed, TvView recreates its SurfaceView internally.
616816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // So we need to call SurfaceHolder.setFixedSize for the new SurfaceView.
617816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            getSurfaceView().getHolder().setFixedSize(mFixedSurfaceWidth, mFixedSurfaceHeight);
618816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
619816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        hideScreenByVideoAvailability(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
620816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        unblockScreenByContentRating();
621816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channel.isPassthrough()) {
622816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mBlockScreenForTuneView.setVisibility(View.GONE);
623816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (mParentControlEnabled) {
624816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mBlockScreenForTuneView.setVisibility(View.VISIBLE);
6259bb548ba08d7e222f276287fead545c6a11704d8Jae Seo        }
6269bb548ba08d7e222f276287fead545c6a11704d8Jae Seo        if (mOnTuneListener != null) {
627816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mOnTuneListener.onStreamInfoChanged(this);
628900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
629900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        return true;
630900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
631900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
63287c1cbb508c96538d1e69ba1fdf3d9f4d52710d6Jaewan Kim    @Override
633816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public Channel getCurrentChannel() {
634816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mCurrentChannel;
635900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
636900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
637816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
638816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Sets the current channel. Call this method only when setting the current channel without
639816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * actually tuning to it.
640816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
641816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param currentChannel The new current channel to set to.
642816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
643816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setCurrentChannel(Channel currentChannel) {
644816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mCurrentChannel = currentChannel;
645b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kang    }
646b47a0ad3227b940175fa4ecfe5c4113da72c8237Dongwon Kang
647561696c364ee330f4316544f1f85498393883526Jae Seo    public void setStreamVolume(float volume) {
648900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        if (!mStarted) {
649900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho            throw new IllegalStateException("TvView isn't started");
650900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho        }
651d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        if (DEBUG) Log.d(TAG, "setStreamVolume " + volume);
652d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mVolume = volume;
653d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        if (!mIsMuted) {
654d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten            mTvView.setStreamVolume(volume);
655d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        }
656900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
657900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho
658816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
659816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Sets fixed size for the internal {@link android.view.Surface} of
660816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@link android.media.tv.TvView}. If either {@code width} or {@code height} is non positive,
661816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * the {@link android.view.Surface}'s size will be matched to the layout.
662816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
663816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Note: Once {@link android.view.SurfaceHolder#setFixedSize} is called,
664816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@link android.view.SurfaceView} and its underlying window can be misaligned, when the size
665816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * of {@link android.view.SurfaceView} is changed without changing either left position or top
66648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * position. For detail, please refer the codes of android.view.SurfaceView.updateWindow().
667816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
668816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setFixedSurfaceSize(int width, int height) {
669816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFixedSurfaceWidth = width;
670816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFixedSurfaceHeight = height;
671816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mFixedSurfaceWidth > 0 && mFixedSurfaceHeight > 0) {
672816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // When the input is changed, TvView recreates its SurfaceView internally.
673816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // So we need to call SurfaceHolder.setFixedSize for the new SurfaceView.
674816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            SurfaceView surfaceView = (SurfaceView) mTvView.getChildAt(0);
675816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            surfaceView.getHolder().setFixedSize(mFixedSurfaceWidth, mFixedSurfaceHeight);
676816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
677816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            SurfaceView surfaceView = (SurfaceView) mTvView.getChildAt(0);
678816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            surfaceView.getHolder().setSizeFromLayout();
679816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
680816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
681816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
682829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    @Override
683829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    public boolean dispatchKeyEvent(KeyEvent event) {
684d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        return mCanReceiveInputEvent && mTvView.dispatchKeyEvent(event);
685829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    }
686829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho
687829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    @Override
688829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    public boolean dispatchTouchEvent(MotionEvent event) {
689d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        return mCanReceiveInputEvent && mTvView.dispatchTouchEvent(event);
690829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    }
691829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho
692829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    @Override
693829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    public boolean dispatchTrackballEvent(MotionEvent event) {
694d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        return mCanReceiveInputEvent && mTvView.dispatchTrackballEvent(event);
695829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    }
696829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho
697829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    @Override
698829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    public boolean dispatchGenericMotionEvent(MotionEvent event) {
699d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        return mCanReceiveInputEvent && mTvView.dispatchGenericMotionEvent(event);
700829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho    }
701829ca34b57d208a84430b2ccecd8975598d086daYoungsang Cho
702900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    public interface OnTuneListener {
703816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onTuneFailed(Channel channel);
704816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onUnexpectedStop(Channel channel);
70521abb13274f379e69c3c86d51e493122cccca345Dongwon Kang        void onStreamInfoChanged(StreamInfo info);
706816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onChannelRetuned(Uri channel);
707816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onContentBlocked();
708816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        void onContentAllowed();
709816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
710816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
71148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void unblockContent(TvContentRating rating) {
71248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
71348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            try {
71448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                Method method = TvView.class.getMethod("requestUnblockContent",
71548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        TvContentRating.class);
71648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                method.invoke(mTvView, rating);
71748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
71848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                e.printStackTrace();
71948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
72048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        } else {
72148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mTvView.unblockContent(rating);
72248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
72321abb13274f379e69c3c86d51e493122cccca345Dongwon Kang    }
72421abb13274f379e69c3c86d51e493122cccca345Dongwon Kang
72521abb13274f379e69c3c86d51e493122cccca345Dongwon Kang    @Override
726e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    public int getVideoWidth() {
727e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim        return mVideoWidth;
728e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    }
729e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim
730e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    @Override
731e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    public int getVideoHeight() {
732e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim        return mVideoHeight;
733e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    }
734e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim
735e06ded45d6e3b83e1a325e97ccd168c6d1c0d2deJaewan Kim    @Override
73621abb13274f379e69c3c86d51e493122cccca345Dongwon Kang    public int getVideoDefinitionLevel() {
73721abb13274f379e69c3c86d51e493122cccca345Dongwon Kang        return mVideoFormat;
738900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho    }
739e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang
740e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    @Override
741816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public float getVideoFrameRate() {
742816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mVideoFrameRate;
743816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
744816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
74548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
74648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Returns displayed aspect ratio (video width / video height * pixel ratio).
74748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
74848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
74948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public float getVideoDisplayAspectRatio() {
75048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mVideoDisplayAspectRatio;
75148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
75248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
753816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
754e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    public int getAudioChannelCount() {
755e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang        return mAudioChannelCount;
756e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    }
757e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang
758e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    @Override
759e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    public boolean hasClosedCaption() {
760e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang        return mHasClosedCaption;
761e51a084c2518f17cee43c86e2f9df907d1f2cafbDongwon Kang    }
7624e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim
7634e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    @Override
7644e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    public boolean isVideoAvailable() {
765816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mVideoAvailable;
7664e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    }
7674e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim
7684e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    @Override
7694e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    public int getVideoUnavailableReason() {
7704e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim        return mVideoUnavailableReason;
7714e5a6a47adb40039a27536236656204f9ad12df3Jaewan Kim    }
772d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
773816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
774816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the {@link android.view.SurfaceView} of the {@link android.media.tv.TvView}.
775816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
77648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private SurfaceView getSurfaceView() {
777816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return (SurfaceView) mTvView.getChildAt(0);
778816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
779816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
780d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) {
781d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mTvView.setOnUnhandledInputEventListener(listener);
782d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
783d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
784816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setClosedCaptionEnabled(boolean enabled) {
785816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.setCaptionEnabled(enabled);
786816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
787816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
788ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang    public List<TvTrackInfo> getTracks(int type) {
789ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang        return mTvView.getTracks(type);
790d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
791d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
792816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public String getSelectedTrack(int type) {
793816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mTvView.getSelectedTrack(type);
794816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
795816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
796ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang    public void selectTrack(int type, String trackId) {
797ec9bd88036279df7421321b2e9945adf8f89db4bDongwon Kang        mTvView.selectTrack(type, trackId);
798d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
799d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
800816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
801816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns if the screen is blocked by {@link #blockScreen()}.
802816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
803816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isScreenBlocked() {
804816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mScreenBlocked;
805816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
806816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
807816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setOnScreenBlockedListener(OnScreenBlockingChangedListener listener) {
808816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mOnScreenBlockedListener = listener;
809816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
810816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
811816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
812816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns currently blocked content rating. {@code null} if it's not blocked.
813816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
814816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public TvContentRating getBlockedContentRating() {
815816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mBlockedContentRating;
816816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
817816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
818816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
819816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Locks current TV screen and mutes.
820816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * There would be black screen with lock icon in order to show that
821816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * screen block is intended and not an error.
822816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * TODO: Accept parameter to show lock icon or not.
823816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
824816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void blockScreen() {
825816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mScreenBlocked = true;
826816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        checkBlockScreenAndMuteNeeded();
827816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mOnScreenBlockedListener != null) {
828816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mOnScreenBlockedListener.onScreenBlockingChanged(true);
829816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
830816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
831816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
832816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void blockScreenByContentRating(TvContentRating rating) {
833816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBlockedContentRating = rating;
834816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        checkBlockScreenAndMuteNeeded();
835816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
836816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
837816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
838816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @SuppressLint("RtlHardcoded")
839816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
840816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.onLayout(changed, left, top, right, bottom);
841816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mIsPip) {
842816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int height = bottom - top;
843816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            float scale;
844816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mBlockScreenType == BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW) {
845816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                scale = height * PIP_BLOCK_SCREEN_SCALE_FACTOR / mShrunkenTvViewHeight;
846816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else {
847816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                scale = height * PIP_BLOCK_SCREEN_SCALE_FACTOR / mScreenHeight;
848816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
849816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // TODO: need to get UX confirmation.
85048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenView.scaleContainerView(scale);
851816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
852816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
853816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
854816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
855816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setLayoutParams(ViewGroup.LayoutParams params) {
856816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.setLayoutParams(params);
857816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTvView != null) {
858816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            copyLayoutParamsToTvView();
859816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
860816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
861816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
862816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void copyLayoutParamsToTvView() {
863816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
864816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        FrameLayout.LayoutParams tvViewLp = (FrameLayout.LayoutParams) mTvView.getLayoutParams();
865816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (tvViewLp.bottomMargin != lp.bottomMargin
866816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.topMargin != lp.topMargin
867816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.leftMargin != lp.leftMargin
868816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.rightMargin != lp.rightMargin
869816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.gravity != lp.gravity
870816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.height != lp.height
871816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                || tvViewLp.width != lp.width) {
872816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (lp.topMargin == tvViewLp.topMargin && lp.leftMargin == tvViewLp.leftMargin) {
873816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // HACK: If top and left position aren't changed and SurfaceHolder.setFixedSize is
874816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // used, SurfaceView doesn't catch the width and height change. It causes a bug that
875816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // PIP size change isn't shown when PIP is located TOP|LEFT. So we adjust 1 px for
876816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // small size PIP as a workaround.
877816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                tvViewLp.leftMargin = lp.leftMargin + 1;
878816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            } else {
879816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                tvViewLp.leftMargin = lp.leftMargin;
880816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
881816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.topMargin = lp.topMargin;
882816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.bottomMargin = lp.bottomMargin;
883816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.rightMargin = lp.rightMargin;
884816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.gravity = lp.gravity;
885816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.height = lp.height;
886816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            tvViewLp.width = lp.width;
887816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTvView.setLayoutParams(tvViewLp);
888816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
889816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
890816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
891816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @Override
892816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    protected void onVisibilityChanged(View changedView, int visibility) {
893816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        super.onVisibilityChanged(changedView, visibility);
894816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTvView != null) {
895816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTvView.setVisibility(visibility);
896816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
897816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
898816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
899816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
900816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Set the type of block screen. If {@code type} is set to {@code BLOCK_SCREEN_TYPE_NO_UI}, the
901816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * block screen will not show any description such as a lock icon and a text for the blocked
902816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * reason, if {@code type} is set to {@code BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW}, the block screen
903816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * will show the description for shrunken tv view (Small icon and short text), and if
904816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@code type} is set to {@code BLOCK_SCREEN_TYPE_NORMAL}, the block screen will show the
905816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * description for normal tv view (Big icon and long text).
906816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
907816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param type The type of block screen to set.
908816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
90948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void setBlockScreenType(@BlockScreenType int type) {
910816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // TODO: need to support the transition from NORMAL to SHRUNKEN and vice verse.
911816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mBlockScreenType != type) {
912816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mBlockScreenType = type;
913816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            updateBlockScreenUI(true);
914816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
915816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
916816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
917816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void updateBlockScreenUI(boolean animation) {
91848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView.endAnimations();
919816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
920816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!mScreenBlocked && mBlockedContentRating == null) {
921816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mBlockScreenView.setVisibility(GONE);
922816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
923816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
924816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
925816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBlockScreenView.setVisibility(VISIBLE);
92648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (!animation || mBlockScreenType != TunableTvView.BLOCK_SCREEN_TYPE_NO_UI) {
92748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            adjustBlockScreenSpacingAndText();
928816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
92948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView.onBlockStatusChanged(mBlockScreenType, animation);
930816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
931816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
93248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private void adjustBlockScreenSpacingAndText() {
933816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // TODO: need to add animation for padding change when the block screen type is changed
934816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // NORMAL to SHRUNKEN and vice verse.
93548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mBlockScreenView.setSpacing(mBlockScreenType);
93648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        String text = getBlockScreenText();
93748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (text != null) {
93848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mBlockScreenView.setText(text);
93948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
94048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
941816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
94248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    /**
94348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Returns the block screen text corresponding to the current status.
94448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Note that returning {@code null} value means that the current text should not be changed.
94548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     */
94648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private String getBlockScreenText() {
947816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mScreenBlocked) {
948816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            switch (mBlockScreenType) {
949816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_NO_UI:
950816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
95148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    return "";
952816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_NORMAL:
9537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    if (mCanModifyParentalControls) {
95448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        return getResources().getString(R.string.tvview_channel_locked);
9557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    } else {
95648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        return getResources().getString(
95748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                R.string.tvview_channel_locked_no_permission);
9587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
959816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
960816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else if (mBlockedContentRating != null) {
961816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            String name = mContentRatingsManager.getDisplayNameForRating(mBlockedContentRating);
962816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            switch (mBlockScreenType) {
963816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_NO_UI:
96448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    return "";
965816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_SHRUNKEN_TV_VIEW:
966816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (TextUtils.isEmpty(name)) {
96748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        return getResources().getString(R.string.shrunken_tvview_content_locked);
968816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    } else {
96948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        return getContext().getString(
97048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                R.string.shrunken_tvview_content_locked_format, name);
971816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
972816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                case BLOCK_SCREEN_TYPE_NORMAL:
973816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (TextUtils.isEmpty(name)) {
9747d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        if (mCanModifyParentalControls) {
97548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            return getResources().getString(R.string.tvview_content_locked);
9767d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        } else {
97748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            return getResources().getString(
9787d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                                    R.string.tvview_content_locked_no_permission);
9797d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        }
980816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    } else {
9817d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        if (mCanModifyParentalControls) {
98248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            return getContext().getString(
98348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                    R.string.tvview_content_locked_format, name);
9847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        } else {
98548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                            return getContext().getString(
98648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                    R.string.tvview_content_locked_format_no_permission, name);
9877d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        }
988816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
989816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
990816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
99148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return null;
992816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
993816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
994816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void checkBlockScreenAndMuteNeeded() {
995816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        updateBlockScreenUI(false);
996816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mScreenBlocked || mBlockedContentRating != null) {
997816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mute();
998816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mIsPip) {
999816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // If we don't make mTvView invisible, some frames are leaked when a user changes
1000816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // PIP layout in options.
1001816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // Note: When video is unavailable, we keep the mTvView's visibility, because
1002816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // TIS implementation may not send video available with no surface.
1003816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mTvView.setVisibility(View.INVISIBLE);
1004816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
1005816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
1006816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            unmuteIfPossible();
1007816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mIsPip) {
1008816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mTvView.setVisibility(View.VISIBLE);
1009816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
1010816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1011816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1012816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1013816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void unblockScreen() {
1014816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mScreenBlocked = false;
1015816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        checkBlockScreenAndMuteNeeded();
1016816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mOnScreenBlockedListener != null) {
1017816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mOnScreenBlockedListener.onScreenBlockingChanged(false);
1018816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1019816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1020816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1021816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void unblockScreenByContentRating() {
1022816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBlockedContentRating = null;
1023816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        checkBlockScreenAndMuteNeeded();
1024816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1025816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
102648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @UiThread
1027816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void hideScreenByVideoAvailability(int reason) {
102848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoAvailable = false;
102948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mVideoUnavailableReason = reason;
103048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mInternetCheckTask != null) {
103148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mInternetCheckTask.cancel(true);
103248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mInternetCheckTask = null;
103348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
1034d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        switch (reason) {
10357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            case TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY:
10367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mHideScreenView.setVisibility(VISIBLE);
103748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setImageVisibility(false);
10387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mHideScreenView.setText(R.string.tvview_msg_audio_only);
10397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mBufferingSpinnerView.setVisibility(GONE);
10407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                unmuteIfPossible();
10417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                break;
1042816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            case TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING:
1043816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mBufferingSpinnerView.setVisibility(VISIBLE);
10447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mute();
1045d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten                break;
10463a72b93e554bd22a5c64e71a6956d9604ce05108Youngsang Cho            case TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL:
104748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setVisibility(VISIBLE);
104848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setText(R.string.tvview_msg_weak_signal);
104948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mBufferingSpinnerView.setVisibility(GONE);
105048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mute();
105148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                break;
105248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            case TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING:
1053816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            case VIDEO_UNAVAILABLE_REASON_NOT_TUNED:
105448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setVisibility(VISIBLE);
105548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setImageVisibility(false);
105648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setText(null);
105748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mBufferingSpinnerView.setVisibility(GONE);
105848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mute();
105948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                break;
106048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            case TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN:
1061816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            default:
1062816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mHideScreenView.setVisibility(VISIBLE);
106348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setImageVisibility(false);
10647d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mHideScreenView.setText(null);
1065816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mBufferingSpinnerView.setVisibility(GONE);
10667d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mute();
106748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                if (mCurrentChannel != null && !mCurrentChannel.isPhysicalTunerChannel()) {
106848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mInternetCheckTask = new InternetCheckTask();
106948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    mInternetCheckTask.execute();
107048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                }
1071d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten                break;
1072d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        }
1073d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
1074d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
1075816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void unhideScreenByVideoAvailability() {
1076816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mVideoAvailable = true;
1077816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mHideScreenView.setVisibility(GONE);
1078816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mBufferingSpinnerView.setVisibility(GONE);
1079816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        unmuteIfPossible();
1080816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1081816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1082816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void unmuteIfPossible() {
1083816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mVideoAvailable && !mScreenBlocked && mBlockedContentRating == null) {
1084816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            unmute();
1085816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1086d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
1087d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
1088d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    private void mute() {
1089d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mIsMuted = true;
1090d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mTvView.setStreamVolume(0);
1091d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
1092d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
1093d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    private void unmute() {
1094d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mIsMuted = false;
1095d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten        mTvView.setStreamVolume(mVolume);
1096d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
1097d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten
1098816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /** Returns true if this view is faded out. */
1099816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isFadedOut() {
1100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mFadeState == FADED_OUT;
1101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /** Fade out this TunableTvView. Fade out by increasing the dimming. */
1104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void fadeOut(int durationMillis, TimeInterpolator interpolator,
1105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final Runnable actionAfterFade) {
1106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.setAlpha(0f);
1107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.setVisibility(View.VISIBLE);
1108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.animate()
1109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .alpha(1f)
1110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .setDuration(durationMillis)
1111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .setInterpolator(interpolator)
1112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .withStartAction(new Runnable() {
1113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
1114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
1115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mFadeState = FADING_OUT;
1116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mActionAfterFade = actionAfterFade;
1117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
1118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                })
1119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .withEndAction(new Runnable() {
1120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
1121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
1122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mFadeState = FADED_OUT;
1123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
1124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                });
1125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /** Fade in this TunableTvView. Fade in by decreasing the dimming. */
1128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void fadeIn(int durationMillis, TimeInterpolator interpolator,
1129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            final Runnable actionAfterFade) {
1130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.setAlpha(1f);
1131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.setVisibility(View.VISIBLE);
1132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.animate()
1133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .alpha(0f)
1134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .setDuration(durationMillis)
1135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .setInterpolator(interpolator)
1136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .withStartAction(new Runnable() {
1137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
1138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
1139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mFadeState = FADING_IN;
1140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mActionAfterFade = actionAfterFade;
1141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
1142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                })
1143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                .withEndAction(new Runnable() {
1144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    @Override
1145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    public void run() {
1146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mFadeState = FADED_IN;
1147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mDimScreenView.setVisibility(View.GONE);
1148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
1149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                });
1150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /** Remove the fade effect. */
1153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void removeFadeEffect() {
1154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.animate().cancel();
1155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mDimScreenView.setVisibility(View.GONE);
1156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFadeState = FADED_IN;
1157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Sets the TimeShiftListener
1161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
1162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param listener The instance of {@link TimeShiftListener}.
1163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void setTimeShiftListener(TimeShiftListener listener) {
1165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimeShiftListener = listener;
1166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void setTimeShiftAvailable(boolean isTimeShiftAvailable) {
1169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || mTimeShiftAvailable == isTimeShiftAvailable) {
1170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTimeShiftAvailable = isTimeShiftAvailable;
1173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (isTimeShiftAvailable) {
117448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() {
1175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
1176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
1177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mTimeShiftListener != null && mCurrentChannel != null
1178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                            && mCurrentChannel.getInputId().equals(inputId)) {
1179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        mTimeShiftListener.onRecordStartTimeChanged(timeMs);
1180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
1181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
1182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                @Override
1184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
1185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    mTimeShiftCurrentPositionMs = timeMs;
1186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
1187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            });
1188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
1189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTvView.setTimeShiftPositionCallback(null);
1190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTimeShiftListener != null) {
1192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mTimeShiftListener.onAvailabilityChanged();
1193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns if the time shift is available for the current channel.
1198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public boolean isTimeShiftAvailable() {
1200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mTimeShiftAvailable;
1201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the current time-shift state. It returns one of {@link #TIME_SHIFT_STATE_NONE},
1205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@link #TIME_SHIFT_STATE_PLAY}, {@link #TIME_SHIFT_STATE_PAUSE},
1206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * {@link #TIME_SHIFT_STATE_REWIND}, {@link #TIME_SHIFT_STATE_FAST_FORWARD}
1207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * or {@link #TIME_SHIFT_STATE_PAUSE}.
1208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    @TimeShiftState public int getTimeShiftState() {
1210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mTimeShiftState;
1211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Plays the media, if the current input supports time-shifting.
1215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void timeshiftPlay() {
1217ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!isTimeShiftAvailable()) {
1222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTimeShiftState == TIME_SHIFT_STATE_PLAY) {
1225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.timeShiftResume();
1228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Pauses the media, if the current input supports time-shifting.
1232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void timeshiftPause() {
1234ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!isTimeShiftAvailable()) {
1239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mTimeShiftState == TIME_SHIFT_STATE_PAUSE) {
1242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.timeShiftPause();
1245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Rewinds the media with the given speed, if the current input supports time-shifting.
1249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
1250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param speed The speed to rewind the media. e.g. 2 for 2x, 3 for 3x and 4 for 4x.
1251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void timeshiftRewind(int speed) {
1253ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1255ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        } else if (!isTimeShiftAvailable()) {
1256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1257ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        } else {
1258ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (speed <= 0) {
1259ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                throw new IllegalArgumentException("The speed should be a positive integer.");
1260ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
1261ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mTimeShiftState = TIME_SHIFT_STATE_REWIND;
1262ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            PlaybackParams params = new PlaybackParams();
1263ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            params.setSpeed(speed * -1);
1264ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mTvView.timeShiftSetPlaybackParams(params);
1265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Fast-forwards the media with the given speed, if the current input supports time-shifting.
1270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
1271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param speed The speed to forward the media. e.g. 2 for 2x, 3 for 3x and 4 for 4x.
1272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void timeshiftFastForward(int speed) {
1274ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1276ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        } else if (!isTimeShiftAvailable()) {
1277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1278ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        } else {
1279ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if (speed <= 0) {
1280ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                throw new IllegalArgumentException("The speed should be a positive integer.");
1281ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
1282ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mTimeShiftState = TIME_SHIFT_STATE_FAST_FORWARD;
1283ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            PlaybackParams params = new PlaybackParams();
1284ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            params.setSpeed(speed);
1285ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            mTvView.timeShiftSetPlaybackParams(params);
1286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Seek to the given time position.
1291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
1292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param timeMs The time in milliseconds to seek to.
1293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void timeshiftSeekTo(long timeMs) {
1295ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
1298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!isTimeShiftAvailable()) {
1300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvView.timeShiftSeekTo(timeMs);
1303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Returns the current playback position in milliseconds.
1307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public long timeshiftGetCurrentPositionMs() {
1309ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
1310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.w(TAG, "Time shifting is not supported in this platform.");
1311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return INVALID_TIME;
1312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (!isTimeShiftAvailable()) {
1314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Time-shift is not supported for the current channel");
1315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) {
1317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            Log.d(TAG, "timeshiftGetCurrentPositionMs: current position ="
1318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    + Utils.toTimeString(mTimeShiftCurrentPositionMs));
1319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
1320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mTimeShiftCurrentPositionMs;
1321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Used to receive the time-shift events.
1325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static abstract class TimeShiftListener {
1327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
1328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when the availability of the time-shift for the current channel has been changed.
1329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * It should be guaranteed that this is called only when the availability is really changed.
1330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
1331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public abstract void onAvailabilityChanged();
1332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
1334ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko         * Called when the record start time has been changed.
1335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
1336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public abstract void onRecordStartTimeChanged(long recordStartTimeMs);
1337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
1338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
1340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * A listener which receives the notification when the screen is blocked/unblocked.
1341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
1342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public static abstract class OnScreenBlockingChangedListener {
1343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        /**
1344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         * Called when the screen is blocked/unblocked.
1345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko         */
1346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public abstract void onScreenBlockingChanged(boolean blocked);
1347d82ca62a0fefaed41493540df7d622cfcfb2cb6cChristofer Åkersten    }
134848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
134948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public class InternetCheckTask extends AsyncTask<Void, Void, Boolean> {
135048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        @Override
135148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        protected Boolean doInBackground(Void... params) {
135248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            return NetworkUtils.isNetworkAvailable(mConnectivityManager);
135348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
135448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
135548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        @Override
135648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        protected void onPostExecute(Boolean networkAvailable) {
135748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mInternetCheckTask = null;
135848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (!mVideoAvailable && !networkAvailable && isAttachedToWindow()
135948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    && mVideoUnavailableReason == TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN) {
136048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setImageVisibility(true);
136148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setImage(R.drawable.ic_sad_cloud);
136248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                mHideScreenView.setText(R.string.tvview_msg_no_internet_connection);
136348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
136448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
136548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
1366900cc7509f1edf86db9ba1e4b71b7d1096769a0aYoungsang Cho}
1367