TimeShiftManager.java revision 816a4be1a0f34f6a48877c8afd3dbbca19eac435
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 17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv; 18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.annotation.SuppressLint; 20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.ContentResolver; 21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context; 22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Handler; 23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.Message; 24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.IntDef; 25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.NonNull; 26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.Nullable; 27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.support.annotation.VisibleForTesting; 28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log; 29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Range; 30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Channel; 32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.OnCurrentProgramUpdatedListener; 33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Program; 34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ProgramDataManager; 35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.ui.TunableTvView; 36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.ui.TunableTvView.TimeShiftListener; 37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.AsyncDbTask; 38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils; 39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.lang.annotation.Retention; 41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.lang.annotation.RetentionPolicy; 42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList; 43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Collections; 44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Iterator; 45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.LinkedList; 46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List; 47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Objects; 48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Queue; 49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.TimeUnit; 50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/** 52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * A class which manages the time shift feature in Live Channels. It consists of two parts. 53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link PlayController} controls the playback such as play/pause, rewind and fast-forward using 54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link TunableTvView} which communicates with TvInputService through 55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link android.media.tv.TvInputService.Session}. 56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramManager} loads programs of the current channel in the background. 57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class TimeShiftManager { 59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String TAG = "TimeShiftManager"; 60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final boolean DEBUG = false; 61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Retention(RetentionPolicy.SOURCE) 63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @IntDef({PLAY_STATUS_PAUSED, PLAY_STATUS_PLAYING}) 64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public @interface PlayStatus {} 65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_STATUS_PAUSED = 0; 66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_STATUS_PLAYING = 1; 67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Retention(RetentionPolicy.SOURCE) 69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @IntDef({PLAY_SPEED_1X, PLAY_SPEED_2X, PLAY_SPEED_3X, PLAY_SPEED_4X, PLAY_SPEED_5X}) 70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public @interface PlaySpeed{} 71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_SPEED_1X = 1; 72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_SPEED_2X = 2; 73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_SPEED_3X = 3; 74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_SPEED_4X = 4; 75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_SPEED_5X = 5; 76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int SHORT_PROGRAM_THRESHOLD_MILLIS = 46 * 60 * 1000; // 46 mins. 78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int[] SHORT_PROGRAM_SPEED_FACTORS = new int[] {2, 4, 12, 48}; 79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int[] LONG_PROGRAM_SPEED_FACTORS = new int[] {2, 8, 32, 128}; 80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Retention(RetentionPolicy.SOURCE) 82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @IntDef({PLAY_DIRECTION_FORWARD, PLAY_DIRECTION_BACKWARD}) 83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public @interface PlayDirection{} 84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_DIRECTION_FORWARD = 0; 85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int PLAY_DIRECTION_BACKWARD = 1; 86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Retention(RetentionPolicy.SOURCE) 88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @IntDef({TIME_SHIFT_ACTION_ID_PLAY, TIME_SHIFT_ACTION_ID_PAUSE, TIME_SHIFT_ACTION_ID_REWIND, 89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TIME_SHIFT_ACTION_ID_FAST_FORWARD, TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS, 90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT}) 91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public @interface TimeShiftActionId{} 92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_PLAY = 1; 93816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_PAUSE = 1 << 1; 94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_REWIND = 1 << 2; 95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_FAST_FORWARD = 1 << 3; 96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS = 1 << 4; 97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public static final int TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT = 1 << 5; 98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int MSG_GET_CURRENT_POSITION = 1000; 100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final int MSG_PREFETCH_PROGRAM = 1001; 101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long REQUEST_CURRENT_POSITION_INTERVAL = TimeUnit.SECONDS.toMillis(1); 102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long MAX_DUMMY_PROGRAM_DURATION = TimeUnit.MINUTES.toMillis(30); 103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko static final long INVALID_TIME = -1; 105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long PREFETCH_TIME_OFFSET_FROM_PROGRAM_END = TimeUnit.MINUTES.toMillis(1); 106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long PREFETCH_DURATION_FOR_NEXT = TimeUnit.HOURS.toMillis(2); 107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko static final long REQUEST_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3); 110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the user presses the {@link android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS} button within 113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * this threshold from the program start time, the play position moves to the start of the 114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * previous program. 115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Otherwise, the play position moves to the start of the current program. 116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * This value is specified in the UX document. 117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long PROGRAM_START_TIME_THRESHOLD = TimeUnit.SECONDS.toMillis(3); 119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the current position enters within this range from the recording start time, rewind action 121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * and jump to previous action is disabled. 122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Similarly, if the current position enters within this range from the current system time, 123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * fast forward action and jump to next action is disabled. 124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * It must be three times longer than {@link #REQUEST_CURRENT_POSITION_INTERVAL} at least. 125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long DISABLE_ACTION_THRESHOLD = 3 * REQUEST_CURRENT_POSITION_INTERVAL; 127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the current position goes out of this range from the recording start time, rewind action 129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * and jump to previous action is enabled. 130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Similarly, if the current position goes out of this range from the current system time, 131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * fast forward action and jump to next action is enabled. 132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Enable threshold and disable threshold must be different because the current position 133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * does not have the continuous value. It changes every one second. 134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long ENABLE_ACTION_THRESHOLD = 136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko DISABLE_ACTION_THRESHOLD + 3 * REQUEST_CURRENT_POSITION_INTERVAL; 137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The current position sent from TIS can not be exactly the same as the current system time 139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * due to the elapsed time to pass the message from TIS to Live Channels. 140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * So the boundary threshold is necessary. 141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The same goes for the recording start time. 142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * It must be three times longer than {@link #REQUEST_CURRENT_POSITION_INTERVAL} at least. 143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long RECORDING_BOUNDARY_THRESHOLD = 3 * REQUEST_CURRENT_POSITION_INTERVAL; 145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final PlayController mPlayController; 147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ProgramManager mProgramManager; 148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final CurrentPositionMediator mCurrentPositionMediator = new CurrentPositionMediator(); 150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private Listener mListener; 152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final OnCurrentProgramUpdatedListener mOnCurrentProgramUpdatedListener; 153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int mEnabledActionIds = TIME_SHIFT_ACTION_ID_PLAY | TIME_SHIFT_ACTION_ID_PAUSE 154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko | TIME_SHIFT_ACTION_ID_REWIND | TIME_SHIFT_ACTION_ID_FAST_FORWARD 155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko | TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS | TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT; 156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int mLastActionId = 0; 157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // TODO: Remove these variables once API level 23 is available. 159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Context mContext; 160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private Program mCurrentProgram; 162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // This variable is used to block notification while changing the availability status. 163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean mNotificationEnabled; 164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @SuppressLint("HandlerLeak") 166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Handler mHandler = new Handler() { 167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void handleMessage(Message msg) { 169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (msg.what) { 170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case MSG_GET_CURRENT_POSITION: 171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.handleGetCurrentPosition(); 172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case MSG_PREFETCH_PROGRAM: 174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramManager.prefetchPrograms(); 175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }; 179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public TimeShiftManager(Context context, TunableTvView tvView, 181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramDataManager programDataManager, 182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko OnCurrentProgramUpdatedListener onCurrentProgramUpdatedListener) { 183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContext = context; 184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController = new PlayController(tvView); 185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramManager = new ProgramManager(programDataManager); 186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mOnCurrentProgramUpdatedListener = onCurrentProgramUpdatedListener; 187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko tvView.setOnScreenBlockedListener(new TunableTvView.OnScreenBlockingChangedListener() { 188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onScreenBlockingChanged(boolean blocked) { 190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko onAvailabilityChanged(); 191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }); 193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Sets a listener which will receive events from this class. 197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void setListener(Listener listener) { 199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener = listener; 200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Checks if the trick play is available for the current channel. 204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean isAvailable() { 206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.isAvailable(); 207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the current time position in milliseconds. 211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public long getCurrentPositionMs() { 213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentPositionMediator.mCurrentPositionMs; 214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void setCurrentPositionMs(long currentTimeMs) { 217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.onCurrentPositionChanged(currentTimeMs); 218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the start time of the recording in milliseconds. 222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public long getRecordStartTimeMs() { 224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long oldestProgramStartTime = mProgramManager.getOldestProgramStartTime(); 225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return oldestProgramStartTime == INVALID_TIME ? INVALID_TIME 226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko : mPlayController.mRecordStartTimeMs; 227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Plays the media. 231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void play() { 235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_PLAY)) { 236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_PLAY; 239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.play(); 240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Pauses the playback. 245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void pause() { 249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_PAUSE)) { 250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_PAUSE; 253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.pause(); 254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Toggles the playing and paused state. 259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void togglePlayPause() { 263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.togglePlayPause(); 264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Plays the media in backward direction. The playback speed is increased by 1x each time 268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * this is called. The range of the speed is from 2x to 5x. 269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the playing position is considered the same as the record start time, it does nothing 270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void rewind() { 274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND)) { 275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_REWIND; 278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.rewind(); 279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Plays the media in forward direction. The playback speed is increased by 1x each time 284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * this is called. The range of the speed is from 2x to 5x. 285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the playing position is the same as the current time, it does nothing. 286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void fastForward() { 290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD)) { 291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_FAST_FORWARD; 294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.fastForward(); 295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Jumps to the start of the current program. 300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the currently playing position is within 3 seconds 301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * (={@link #PROGRAM_START_TIME_THRESHOLD})from the start time of the program, it goes to 302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * the start of the previous program if exists. 303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the playing position is the same as the record start time, it does nothing. 304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void jumpToPrevious() { 308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS)) { 309816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 310816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mProgramManager.getProgramAt( 312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.mCurrentPositionMs - PROGRAM_START_TIME_THRESHOLD); 313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program == null) { 314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long seekPosition = 317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Math.max(program.getStartTimeUtcMillis(), mPlayController.mRecordStartTimeMs); 318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS; 319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.seekTo(seekPosition); 320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.onSeekRequested(seekPosition); 321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Jumps to the start of the next program if exists. 326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If there's no next program, it jumps to the current system time and shows the live TV. 327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the playing position is considered the same as the current time, it does nothing. 328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @throws IllegalStateException if the trick play is not available. 330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void jumpToNext() { 332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!isActionEnabled(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT)) { 333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program currentProgram = mProgramManager.getProgramAt( 336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.mCurrentPositionMs); 337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (currentProgram == null) { 338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program nextProgram = mProgramManager.getProgramAt(currentProgram.getEndTimeUtcMillis()); 341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long currentTimeMs = System.currentTimeMillis(); 342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mLastActionId = TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT; 343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (nextProgram == null || nextProgram.getStartTimeUtcMillis() > currentTimeMs) { 344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.seekTo(currentTimeMs); 345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayController.isForwarding()) { 346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The current position will be the current system time from now. 347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.mIsPlayOffsetChanged = false; 348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.initialize(currentTimeMs); 349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The current position would not be the current system time. 351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // So need to wait for the correct time from TIS. 352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.onSeekRequested(currentTimeMs); 353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.seekTo(nextProgram.getStartTimeUtcMillis()); 356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.onSeekRequested(nextProgram.getStartTimeUtcMillis()); 357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 358816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 359816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 360816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the playback status. The value is PLAY_STATUS_PAUSED or PLAY_STATUS_PLAYING. 363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlayStatus public int getPlayStatus() { 365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.mPlayStatus; 366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 367816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the displayed playback speed. The value is one of PLAY_SPEED_1X, PLAY_SPEED_2X, 370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * PLAY_SPEED_3X, PLAY_SPEED_4X and PLAY_SPEED_5X. 371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlaySpeed public int getDisplayedPlaySpeed() { 373816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.mDisplayedPlaySpeed; 374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the playback speed. The value is PLAY_DIRECTION_FORWARD or PLAY_DIRECTION_BACKWARD. 378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlayDirection public int getPlayDirection() { 380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.mPlayDirection; 381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the ID of the last action.. 385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @TimeShiftActionId public int getLastActionId() { 387816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mLastActionId; 388816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Enables or disables the time-shift actions. 392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void enableAction(@TimeShiftActionId int actionId, boolean enable) { 395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int oldEnabledActionIds = mEnabledActionIds; 396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (enable) { 397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mEnabledActionIds |= actionId; 398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mEnabledActionIds &= ~actionId; 400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mListener != null 402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && oldEnabledActionIds != mEnabledActionIds) { 403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onActionEnabledChanged(actionId, enable); 404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 407816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean isActionEnabled(@TimeShiftActionId int actionId) { 408816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return (mEnabledActionIds & actionId) == actionId; 409816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 410816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void updateActions() { 412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isAvailable()) { 413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_PLAY, true); 414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_PAUSE, true); 415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Rewind action and jump to previous action. 416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long threshold = isActionEnabled(TIME_SHIFT_ACTION_ID_REWIND) 417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ? DISABLE_ACTION_THRESHOLD : ENABLE_ACTION_THRESHOLD; 418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean enabled = mCurrentPositionMediator.mCurrentPositionMs 419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko - mPlayController.mRecordStartTimeMs > threshold; 420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_REWIND, enabled); 421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS, enabled); 422816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Fast forward action and jump to next action 423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko threshold = isActionEnabled(TIME_SHIFT_ACTION_ID_FAST_FORWARD) 424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ? DISABLE_ACTION_THRESHOLD : ENABLE_ACTION_THRESHOLD; 425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enabled = System.currentTimeMillis() - mCurrentPositionMediator.mCurrentPositionMs 426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko > threshold; 427816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_FAST_FORWARD, enabled); 428816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_NEXT, enabled); 429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_PLAY, false); 431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_PAUSE, false); 432816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_REWIND, false); 433816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_JUMP_TO_PREVIOUS, false); 434816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_FAST_FORWARD, false); 435816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko enableAction(TIME_SHIFT_ACTION_ID_PLAY, false); 436816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 437816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 438816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void updateCurrentProgram() { 440816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program currentProgram = getProgramAt(mCurrentPositionMediator.mCurrentPositionMs); 441816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!Program.isValid(currentProgram)) { 442816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko currentProgram = null; 443816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 444816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!Objects.equals(mCurrentProgram, currentProgram)) { 445816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.d(TAG, "Current program has been updated. " + currentProgram); 446816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentProgram = currentProgram; 447816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mOnCurrentProgramUpdatedListener != null) { 448816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Channel channel = mPlayController.getCurrentChannel(); 449816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel != null) { 450816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mOnCurrentProgramUpdatedListener.onCurrentProgramUpdated(channel.getId(), 451816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentProgram); 452816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.onCurrentProgramChanged(); 453816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 454816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 455816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 456816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 457816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 458816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 459816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns {@code true} if the trick play is available and it's playing to the forward direction 460816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * with normal speed, otherwise {@code false}. 461816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 462816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean isNormalPlaying() { 463816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.isAvailable() 464816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && mPlayController.mPlayStatus == PLAY_STATUS_PLAYING 465816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && mPlayController.mPlayDirection == PLAY_DIRECTION_FORWARD 466816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && mPlayController.mDisplayedPlaySpeed == PLAY_SPEED_1X; 467816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 468816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 469816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Checks if the trick play is available and it's playback status is paused. 471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean isPaused() { 473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayController.isAvailable() && mPlayController.mPlayStatus == PLAY_STATUS_PAUSED; 474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 475816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 476816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the program which airs at the given time. 478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 479816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @NonNull 480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public Program getProgramAt(long timeMs) { 481816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mProgramManager.getProgramAt(timeMs); 482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program == null) { 483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Guard just in case when the program prefetch handler doesn't work on time. 484816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramManager.addDummyProgramsAt(timeMs); 485816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko program = mProgramManager.getProgramAt(timeMs); 486816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 487816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return program; 488816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 489816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 490816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onAvailabilityChanged() { 491816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramManager.onAvailabilityChanged(mPlayController.isAvailable(), 492816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.getCurrentChannel(), mPlayController.mRecordStartTimeMs); 493816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 494816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Availability change notification should be always sent 495816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // even if mNotificationEnabled is false. 496816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mListener != null) { 497816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onAvailabilityChanged(); 498816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 499816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onRecordStartTimeChanged() { 502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayController.isAvailable()) { 503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramManager.onRecordStartTimeChanged(mPlayController.mRecordStartTimeMs); 504816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 505816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 506816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mListener != null) { 507816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onRecordStartTimeChanged(); 508816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 509816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 510816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 511816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onCurrentPositionChanged() { 512816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateActions(); 513816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateCurrentProgram(); 514816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mListener != null) { 515816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onCurrentPositionChanged(); 516816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 517816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 518816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 519816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onPlayStatusChanged(@PlayStatus int status) { 520816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mListener != null) { 521816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onPlayStatusChanged(status); 522816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 523816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 524816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 525816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onProgramInfoChanged() { 526816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updateCurrentProgram(); 527816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mNotificationEnabled && mListener != null) { 528816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.onProgramInfoChanged(); 529816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 530816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 531816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 532816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 533816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Returns the current program which airs right now.<p> 534816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 535816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the program is a dummy program, which means there's no program information, 536816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * returns {@code null}. 537816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 538816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Nullable 539816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public Program getCurrentProgram() { 540816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isAvailable()) { 541816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram; 542816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 543816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 544816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 545816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 546816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int getPlaybackSpeed() { 547816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int[] playbackSpeedList; 548816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (getCurrentProgram() == null || getCurrentProgram().getEndTimeUtcMillis() 549816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko - getCurrentProgram().getStartTimeUtcMillis() > SHORT_PROGRAM_THRESHOLD_MILLIS) { 550816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko playbackSpeedList = LONG_PROGRAM_SPEED_FACTORS; 551816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 552816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko playbackSpeedList = SHORT_PROGRAM_SPEED_FACTORS; 553816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 554816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (mPlayController.mDisplayedPlaySpeed) { 555816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_1X: 556816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return 1; 557816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_2X: 558816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return playbackSpeedList[0]; 559816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_3X: 560816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return playbackSpeedList[1]; 561816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_4X: 562816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return playbackSpeedList[2]; 563816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_5X: 564816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return playbackSpeedList[3]; 565816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko default: 566816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.w(TAG, "Unknown displayed play speed is chosen : " 567816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + mPlayController.mDisplayedPlaySpeed); 568816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return 1; 569816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 570816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 571816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 572816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 573816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * A class which controls the trick play. 574816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 575816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class PlayController { 576816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final TunableTvView mTvView; 577816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 578816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private long mPossibleStartTimeMs; 579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private long mRecordStartTimeMs; 580816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlayStatus private int mPlayStatus = PLAY_STATUS_PAUSED; 582816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlaySpeed private int mDisplayedPlaySpeed = PLAY_SPEED_1X; 583816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @PlayDirection private int mPlayDirection = PLAY_DIRECTION_FORWARD; 584816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int mPlaybackSpeed; 585816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 586816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 587816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Indicates that the trick play is not playing the current time position. 588816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * It is set true when {@link PlayController#pause}, {@link PlayController#rewind}, 589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link PlayController#fastForward} and {@link PlayController#seekTo} 590816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * is called. 591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If it is true, the current time is equal to System.currentTimeMillis(). 592816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 593816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean mIsPlayOffsetChanged; 594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko PlayController(TunableTvView tvView) { 596816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView = tvView; 597816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.setTimeShiftListener(new TimeShiftListener() { 598816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 599816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onAvailabilityChanged() { 600816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Do not send the notifications while the availability is changing, 601816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // because the variables are in the intermediate state. 602816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // For example, the current program can be null. 603816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mNotificationEnabled = false; 604816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_1X; 605816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = 1; 606816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayDirection = PLAY_DIRECTION_FORWARD; 607816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = false; 608816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPossibleStartTimeMs = System.currentTimeMillis(); 609816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mRecordStartTimeMs = mPossibleStartTimeMs; 610816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMediator.initialize(mPossibleStartTimeMs); 611816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.removeMessages(MSG_GET_CURRENT_POSITION); 612816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 613816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isAvailable()) { 614816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // When the media availability message has come. 615816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.setPlayStatus(PLAY_STATUS_PLAYING); 616816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION, 617816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko REQUEST_CURRENT_POSITION_INTERVAL); 618816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 619816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // When the tune command is sent. 620816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayController.setPlayStatus(PLAY_STATUS_PAUSED); 621816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 622816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onAvailabilityChanged(); 623816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mNotificationEnabled = true; 624816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 625816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 626816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 627816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onRecordStartTimeChanged(long recordStartTimeMs) { 628816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (recordStartTimeMs < mPossibleStartTimeMs) { 629816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Do not warn in this case because it can happen in normal cases. 630816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 631816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Record start time is less then the time when it became " 632816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "available. {availableStartTime=" 633816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + Utils.toTimeString(mPossibleStartTimeMs) 634816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + ", recordStartTimeMs=" + Utils.toTimeString(recordStartTimeMs) 635816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + "}"); 636816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 637816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko recordStartTimeMs = mPossibleStartTimeMs; 638816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 639816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mRecordStartTimeMs == recordStartTimeMs) { 640816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 641816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 642816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mRecordStartTimeMs = recordStartTimeMs; 643816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onRecordStartTimeChanged(); 644816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 645816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // According to the UX guidelines, the stream should be resumed if the 646816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // recording buffer fills up while paused, which means that the current time 647816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // position is the same as or before the recording start time. 648816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // But, for this application and the TIS, it's an erroneous and confusing 649816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // situation if the current time position is before the recording start time. 650816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // So, we recommend the TIS to keep the current time position greater than or 651816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // equal to the recording start time. 652816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // And here, we assume that the buffer is full if the current time position 653816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // is nearly equal to the recording start time. 654816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayStatus == PLAY_STATUS_PAUSED && 655816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko getCurrentPositionMs() - mRecordStartTimeMs 656816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko < RECORDING_BOUNDARY_THRESHOLD) { 657816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.play(); 658816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 659816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 660816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }); 661816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 662816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 663816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isAvailable() { 664816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mTvView.isTimeShiftAvailable() && !mTvView.isScreenBlocked(); 665816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 666816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 667816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void handleGetCurrentPosition() { 668816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mIsPlayOffsetChanged) { 669816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long currentTimeMs = System.currentTimeMillis(); 670816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long currentPositionMs = Math.max( 671816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Math.min(mTvView.timeshiftGetCurrentPositionMs(), currentTimeMs), 672816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mRecordStartTimeMs); 673816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isCurrentTime = 674816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko currentTimeMs - currentPositionMs < RECORDING_BOUNDARY_THRESHOLD; 675816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long newCurrentPositionMs; 676816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isCurrentTime && isForwarding()) { 677816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // It's playing forward and the current playing position reached 678816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // the current system time. i.e. The live stream is played. 679816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Therefore no need to call TvView.timeshiftGetCurrentPositionMs 680816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // any more. 681816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko newCurrentPositionMs = currentTimeMs; 682816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = false; 683816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mDisplayedPlaySpeed > PLAY_SPEED_1X) { 684816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.play(); 685816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 686816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 687816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko newCurrentPositionMs = currentPositionMs; 688816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isRecordStartTime = currentPositionMs - mRecordStartTimeMs 689816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko < RECORDING_BOUNDARY_THRESHOLD; 690816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isRecordStartTime && isRewinding()) { 691816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.play(); 692816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 693816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 694816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setCurrentPositionMs(newCurrentPositionMs); 695816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 696816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setCurrentPositionMs(System.currentTimeMillis()); 697816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onCurrentPositionChanged(); 698816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 699816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Need to send message here just in case there is no or invalid response 700816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // for the current time position request from TIS. 701816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_GET_CURRENT_POSITION, 702816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko REQUEST_CURRENT_POSITION_INTERVAL); 703816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 704816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 705816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void play() { 706816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_1X; 707816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = 1; 708816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayDirection = PLAY_DIRECTION_FORWARD; 709816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftPlay(); 710816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setPlayStatus(PLAY_STATUS_PLAYING); 711816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 712816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 713816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void pause() { 714816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_1X; 715816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = 1; 716816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftPause(); 717816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setPlayStatus(PLAY_STATUS_PAUSED); 718816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = true; 719816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 720816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 721816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void togglePlayPause() { 722816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayStatus == PLAY_STATUS_PAUSED) { 723816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko play(); 724816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 725816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko pause(); 726816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 727816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 728816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 729816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void rewind() { 730816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayDirection == PLAY_DIRECTION_BACKWARD) { 731816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko increaseDisplayedPlaySpeed(); 732816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 733816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_2X; 734816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 735816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayDirection = PLAY_DIRECTION_BACKWARD; 736816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = getPlaybackSpeed(); 737816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftRewind(mPlaybackSpeed); 738816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setPlayStatus(PLAY_STATUS_PLAYING); 739816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = true; 740816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 741816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 742816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void fastForward() { 743816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayDirection == PLAY_DIRECTION_FORWARD) { 744816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko increaseDisplayedPlaySpeed(); 745816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 746816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_2X; 747816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 748816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayDirection = PLAY_DIRECTION_FORWARD; 749816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = getPlaybackSpeed(); 750816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftFastForward(mPlaybackSpeed); 751816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko setPlayStatus(PLAY_STATUS_PLAYING); 752816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = true; 753816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 754816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 755816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 756816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Moves to the specified time. 757816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 758816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void seekTo(long timeMs) { 759816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftSeekTo(Math.min(System.currentTimeMillis(), 760816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Math.max(mRecordStartTimeMs, timeMs))); 761816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsPlayOffsetChanged = true; 762816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 763816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 764816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onCurrentProgramChanged() { 765816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Update playback speed 766816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mDisplayedPlaySpeed == PLAY_SPEED_1X) { 767816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 768816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 769816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int playbackSpeed = getPlaybackSpeed(); 770816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (playbackSpeed != mPlaybackSpeed) { 771816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlaybackSpeed = playbackSpeed; 772816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPlayDirection == PLAY_DIRECTION_FORWARD) { 773816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftFastForward(mPlaybackSpeed); 774816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 775816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mTvView.timeshiftRewind(mPlaybackSpeed); 776816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 777816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 778816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 779816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 780816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void increaseDisplayedPlaySpeed() { 781816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (mDisplayedPlaySpeed) { 782816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_1X: 783816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_2X; 784816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 785816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_2X: 786816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_3X; 787816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 788816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_3X: 789816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_4X; 790816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 791816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PLAY_SPEED_4X: 792816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mDisplayedPlaySpeed = PLAY_SPEED_5X; 793816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 794816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 795816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 796816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 797816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void setPlayStatus(@PlayStatus int status) { 798816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPlayStatus = status; 799816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onPlayStatusChanged(status); 800816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 801816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 802816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isForwarding() { 803816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayStatus == PLAY_STATUS_PLAYING && mPlayDirection == PLAY_DIRECTION_FORWARD; 804816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 805816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 806816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean isRewinding() { 807816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPlayStatus == PLAY_STATUS_PLAYING && mPlayDirection == PLAY_DIRECTION_BACKWARD; 808816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 809816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 810816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Channel getCurrentChannel() { 811816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mTvView.getCurrentChannel(); 812816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 813816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 814816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 815816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class ProgramManager { 816816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ProgramDataManager mProgramDataManager; 817816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private Channel mChannel; 818816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final List<Program> mPrograms = new ArrayList<>(); 819816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final Queue<Range<Long>> mProgramLoadQueue = new LinkedList<>(); 820816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private LoadProgramsForCurrentChannelTask mProgramLoadTask = null; 821816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 822816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramManager(ProgramDataManager programDataManager) { 823816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager = programDataManager; 824816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 825816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 826816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onAvailabilityChanged(boolean available, Channel channel, long currentPositionMs) { 827816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadQueue.clear(); 828816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadTask != null) { 829816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask.cancel(true); 830816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 831816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.removeMessages(MSG_PREFETCH_PROGRAM); 832816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.clear(); 833816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannel = channel; 834816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (channel == null || channel.isPassthrough()) { 835816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 836816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 837816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (available) { 838816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mProgramDataManager.getCurrentProgram(channel.getId()); 839816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long prefetchStartTimeMs; 840816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program != null) { 841816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.add(program); 842816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko prefetchStartTimeMs = program.getEndTimeUtcMillis(); 843816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 844816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko prefetchStartTimeMs = Utils.floorTime(currentPositionMs, 845816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko MAX_DUMMY_PROGRAM_DURATION); 846816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 847816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Create dummy program 848816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(createDummyPrograms(prefetchStartTimeMs, 849816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko currentPositionMs + PREFETCH_DURATION_FOR_NEXT)); 850816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko schedulePrefetchPrograms(); 851816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onProgramInfoChanged(); 852816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 853816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 854816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 855816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onRecordStartTimeChanged(long startTimeMs) { 856816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mChannel == null || mChannel.isPassthrough()) { 857816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 858816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 859816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long currentMs = System.currentTimeMillis(); 860816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 861816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long fetchStartTimeMs = Utils.floorTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION); 862816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean needToLoad = addDummyPrograms(fetchStartTimeMs, 863816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko currentMs + PREFETCH_DURATION_FOR_NEXT); 864816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (needToLoad) { 865816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> period = Range.create(fetchStartTimeMs, currentMs); 866816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadQueue.add(period); 867816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startTaskIfNeeded(); 868816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 869816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 870816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 871816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void startTaskIfNeeded() { 872816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadQueue.isEmpty()) { 873816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 874816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 875816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadTask == null || mProgramLoadTask.isCancelled()) { 876816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNext(); 877816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 878816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (mProgramLoadTask.getStatus()) { 879816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case PENDING: 880816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadTask.overlaps(mProgramLoadQueue)) { 881816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadTask.cancel(true)) { 882816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadQueue.add(mProgramLoadTask.getPeriod()); 883816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask = null; 884816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNext(); 885816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 886816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 887816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 888816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case RUNNING: 889816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Remove pending task fully satisfied by the current 890816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> current = mProgramLoadTask.getPeriod(); 891816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Iterator<Range<Long>> i = mProgramLoadQueue.iterator(); 892816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (i.hasNext()) { 893816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> r = i.next(); 894816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (current.contains(r)) { 895816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko i.remove(); 896816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 897816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 898816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 899816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case FINISHED: 900816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The task should have already cleared it self, clear and restart anyways. 901816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.w(TAG, mProgramLoadTask + " is finished, but was not cleared"); 902816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNext(); 903816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 904816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 905816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 906816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 907816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 908816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void startNext() { 909816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask = null; 910816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramLoadQueue.isEmpty()) { 911816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 912816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 913816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 914816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> next = mProgramLoadQueue.poll(); 915816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Extend next to include any overlapping Ranges. 916816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Iterator<Range<Long>> i = mProgramLoadQueue.iterator(); 917816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while(i.hasNext()) { 918816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> r = i.next(); 919816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if(next.contains(r.getLower()) || next.contains(r.getUpper())){ 920816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko i.remove(); 921816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko next = next.extend(r); 922816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 923816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 924816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mChannel != null) { 925816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask = new LoadProgramsForCurrentChannelTask( 926816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContext.getContentResolver(), next); 927816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask.executeOnDbThread(); 928816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 929816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 930816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 931816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void addDummyProgramsAt(long timeMs) { 932816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko addDummyPrograms(timeMs, timeMs + PREFETCH_DURATION_FOR_NEXT); 933816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 934816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 935816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean addDummyPrograms(Range<Long> period) { 936816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return addDummyPrograms(period.getLower(), period.getUpper()); 937816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 938816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 939816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private boolean addDummyPrograms(long startTimeMs, long endTimeMs) { 940816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean added = false; 941816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPrograms.isEmpty()) { 942816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Insert dummy program. 943816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(createDummyPrograms(startTimeMs, endTimeMs)); 944816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return true; 945816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 946816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Insert dummy program to the head of the list if needed. 947816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program firstProgram = mPrograms.get(0); 948816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (startTimeMs < firstProgram.getStartTimeUtcMillis()) { 949816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!firstProgram.isValid()) { 950816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Already the firstProgram is dummy. 951816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.remove(0); 952816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(0, 953816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko createDummyPrograms(startTimeMs, firstProgram.getEndTimeUtcMillis())); 954816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 955816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(0, 956816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko createDummyPrograms(startTimeMs, firstProgram.getStartTimeUtcMillis())); 957816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 958816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko added = true; 959816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 960816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Insert dummy program to the tail of the list if needed. 961816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program lastProgram = mPrograms.get(mPrograms.size() - 1); 962816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (endTimeMs > lastProgram.getEndTimeUtcMillis()) { 963816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!lastProgram.isValid()) { 964816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Already the lastProgram is dummy. 965816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.remove(mPrograms.size() - 1); 966816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll( 967816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko createDummyPrograms(lastProgram.getStartTimeUtcMillis(), endTimeMs)); 968816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 969816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll( 970816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko createDummyPrograms(lastProgram.getEndTimeUtcMillis(), endTimeMs)); 971816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 972816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko added = true; 973816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 974816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Insert dummy programs if the holes exist in the list. 975816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 1; i < mPrograms.size(); ++i) { 976816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endOfPrevious = mPrograms.get(i - 1).getEndTimeUtcMillis(); 977816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startOfCurrent = mPrograms.get(i).getStartTimeUtcMillis(); 978816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (startOfCurrent > endOfPrevious) { 979816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<Program> dummyPrograms = 980816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko createDummyPrograms(endOfPrevious, startOfCurrent); 981816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(i, dummyPrograms); 982816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko i += dummyPrograms.size(); 983816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko added = true; 984816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 985816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 986816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return added; 987816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 988816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 989816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void removeDummyPrograms() { 990816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 0; i < mPrograms.size(); ++i) { 991816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mPrograms.get(i); 992816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (!program.isValid()) { 993816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.remove(i--); 994816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 995816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 996816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 997816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 998816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void removeOverlappedPrograms(List<Program> loadedPrograms) { 999816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPrograms.size() == 0) { 1000816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 1001816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1002816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mPrograms.get(0); 1003816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 0, j = 0; i < mPrograms.size() && j < loadedPrograms.size(); ++j) { 1004816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program loadedProgram = loadedPrograms.get(j); 1005816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Skip previous programs. 1006816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (program.getEndTimeUtcMillis() < loadedProgram.getStartTimeUtcMillis()) { 1007816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Reached end of mPrograms. 1008816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (++i == mPrograms.size()) { 1009816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 1010816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1011816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko program = mPrograms.get(i); 1012816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1013816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Remove overlapped programs. 1014816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (program.getStartTimeUtcMillis() < loadedProgram.getEndTimeUtcMillis() 1015816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && program.getEndTimeUtcMillis() > loadedProgram.getStartTimeUtcMillis()) { 1016816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.remove(i); 1017816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (i >= mPrograms.size()) { 1018816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 1019816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1020816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko program = mPrograms.get(i); 1021816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1022816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1023816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1024816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1025816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Returns a list of dummy programs. 1026816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The maximum duration of a dummy program is {@link MAX_DUMMY_PROGRAM_DURATION}. 1027816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // So if the duration ({@code endTimeMs}-{@code startTimeMs}) is greater than the duration, 1028816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // we need to create multiple dummy programs. 1029816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // The reason of the limitation of the duration is because we want the trick play viewer 1030816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // to show the time-line duration of {@link MAX_DUMMY_PROGRAM_DURATION} at most 1031816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // for a dummy program. 1032816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private List<Program> createDummyPrograms(long startTimeMs, long endTimeMs) { 1033816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (startTimeMs >= endTimeMs) { 1034816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return Collections.emptyList(); 1035816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1036816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<Program> programs = new ArrayList<>(); 1037816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long start = startTimeMs; 1038816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long end = Utils.ceilTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION); 1039816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (end < endTimeMs) { 1040816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programs.add(new Program.Builder() 1041816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .setStartTimeUtcMillis(start) 1042816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .setEndTimeUtcMillis(end) 1043816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .build()); 1044816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko start = end; 1045816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko end += MAX_DUMMY_PROGRAM_DURATION; 1046816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1047816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programs.add(new Program.Builder() 1048816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .setStartTimeUtcMillis(start) 1049816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .setEndTimeUtcMillis(endTimeMs) 1050816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .build()); 1051816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return programs; 1052816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1053816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1054816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program getProgramAt(long timeMs) { 1055816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return getProgramAt(timeMs, 0, mPrograms.size() - 1); 1056816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1057816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1058816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private Program getProgramAt(long timeMs, int start, int end) { 1059816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (start > end) { 1060816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 1061816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1062816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int mid = (start + end) / 2; 1063816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mPrograms.get(mid); 1064816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program.getStartTimeUtcMillis() > timeMs) { 1065816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return getProgramAt(timeMs, start, mid - 1); 1066816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else if (program.getEndTimeUtcMillis() <= timeMs) { 1067816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return getProgramAt(timeMs, mid+1, end); 1068816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 1069816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return program; 1070816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1071816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1072816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1073816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private long getOldestProgramStartTime() { 1074816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPrograms.isEmpty()) { 1075816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return INVALID_TIME; 1076816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1077816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mPrograms.get(0).getStartTimeUtcMillis(); 1078816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1079816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1080816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private Program getLastValidProgram() { 1081816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = mPrograms.size() - 1; i >= 0; --i) { 1082816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mPrograms.get(i); 1083816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program.isValid()) { 1084816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return program; 1085816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1086816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1087816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 1088816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1089816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1090816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void schedulePrefetchPrograms() { 1091816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.d(TAG, "Scheduling prefetching programs."); 1092816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mHandler.hasMessages(MSG_PREFETCH_PROGRAM)) { 1093816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 1094816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1095816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program lastValidProgram = getLastValidProgram(); 1096816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.d(TAG, "Last valid program = " + lastValidProgram); 1097816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (lastValidProgram != null) { 1098816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long delay = lastValidProgram.getEndTimeUtcMillis() 1099816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko - PREFETCH_TIME_OFFSET_FROM_PROGRAM_END - System.currentTimeMillis(); 1100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_PREFETCH_PROGRAM, delay); 1101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.d(TAG, "Scheduling with " + delay + "(ms) delays."); 1102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 1103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.sendEmptyMessage(MSG_PREFETCH_PROGRAM); 1104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) Log.d(TAG, "Scheduling promptly."); 1105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void prefetchPrograms() { 1109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs; 1110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program lastValidProgram = getLastValidProgram(); 1111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (lastValidProgram == null) { 1112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startTimeMs = System.currentTimeMillis(); 1113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 1114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startTimeMs = lastValidProgram.getEndTimeUtcMillis(); 1115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endTimeMs = System.currentTimeMillis() + PREFETCH_DURATION_FOR_NEXT; 1117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 1118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Prefetch task starts: {startTime=" + Utils.toTimeString(startTimeMs) 1119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko + ", endTime=" + Utils.toTimeString(endTimeMs) + "}"); 1120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadQueue.add(Range.create(startTimeMs, endTimeMs)); 1122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startTaskIfNeeded(); 1123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class LoadProgramsForCurrentChannelTask 1126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko extends AsyncDbTask.LoadProgramsForChannelTask { 1127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public LoadProgramsForCurrentChannelTask(ContentResolver contentResolver, 1129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> period) { 1130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super(contentResolver, mChannel.getId(), period); 1131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 1134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko protected void onPostExecute(List<Program> programs) { 1135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 1136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Programs are loaded {channelId=" + mChannelId + 1137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ", from=" + Utils.toTimeString(mPeriod.getLower()) + 1138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ", to=" + Utils.toTimeString(mPeriod.getUpper()) + 1139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko "}"); 1140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko //remove pending tasks that are fully satisfied by this query. 1142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Iterator<Range<Long>> it = mProgramLoadQueue.iterator(); 1143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (it.hasNext()) { 1144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Range<Long> r = it.next(); 1145816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPeriod.contains(r)) { 1146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko it.remove(); 1147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (programs == null || programs.isEmpty() || mPrograms.isEmpty()) { 1150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(programs); 1151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (addDummyPrograms(mPeriod)) { 1152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onProgramInfoChanged(); 1153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko schedulePrefetchPrograms(); 1155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNextLoadingIfNeeded(); 1156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 1157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko removeDummyPrograms(); 1159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko removeOverlappedPrograms(programs); 1160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program loadedProgram = programs.get(0); 1161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 0; i < mPrograms.size() && !programs.isEmpty(); ++i) { 1162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program program = mPrograms.get(i); 1163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (program.getStartTimeUtcMillis() > loadedProgram 1164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .getStartTimeUtcMillis()) { 1165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.add(i++, loadedProgram); 1166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programs.remove(0); 1167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (programs.isEmpty()) { 1168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 1169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko loadedProgram = programs.get(0); 1171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mPrograms.addAll(programs); 1174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko addDummyPrograms(mPeriod); 1175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onProgramInfoChanged(); 1176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko schedulePrefetchPrograms(); 1177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNextLoadingIfNeeded(); 1178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 1181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko protected void onCancelled(List<Program> programs) { 1182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 1183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Program loading has been canceled {channelId=" + (mChannel == null 1184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ? "null" : mChannelId) + ", from=" + Utils 1185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .toTimeString(mPeriod.getLower()) + ", to=" + Utils 1186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko .toTimeString(mPeriod.getUpper()) + "}"); 1187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1188816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startNextLoadingIfNeeded(); 1189816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1191816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void startNextLoadingIfNeeded() { 1192816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramLoadTask = null; 1193816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Need to post to handler, because the task is still running. 1194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandler.post(new Runnable() { 1195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 1196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void run() { 1197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startTaskIfNeeded(); 1198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko }); 1200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean overlaps(Queue<Range<Long>> programLoadQueue) { 1203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Range<Long> r : programLoadQueue) { 1204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mPeriod.contains(r.getLower()) || mPeriod.contains(r.getUpper())) { 1205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return true; 1206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 1209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1213816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @VisibleForTesting 1214816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final class CurrentPositionMediator { 1215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long mCurrentPositionMs; 1216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long mSeekRequestTimeMs; 1217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void initialize(long timeMs) { 1219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mSeekRequestTimeMs = INVALID_TIME; 1220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMs = timeMs; 1221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onCurrentPositionChanged(); 1222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onSeekRequested(long seekTimeMs) { 1225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mSeekRequestTimeMs = System.currentTimeMillis(); 1226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMs = seekTimeMs; 1227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onCurrentPositionChanged(); 1228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onCurrentPositionChanged(long currentPositionMs) { 1231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mSeekRequestTimeMs == INVALID_TIME) { 1232816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMs = currentPositionMs; 1233816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onCurrentPositionChanged(); 1234816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 1235816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1236816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long currentTimeMs = System.currentTimeMillis(); 1237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isValid = Math.abs(currentPositionMs - mCurrentPositionMs) < REQUEST_TIMEOUT_MS; 1238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko boolean isTimeout = currentTimeMs > mSeekRequestTimeMs + REQUEST_TIMEOUT_MS; 1239816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (isValid || isTimeout) { 1240816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko initialize(currentPositionMs); 1241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 1242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (getPlayStatus() == PLAY_STATUS_PLAYING) { 1243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (getPlayDirection() == PLAY_DIRECTION_FORWARD) { 1244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMs += (currentTimeMs - mSeekRequestTimeMs) 1245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * getPlaybackSpeed(); 1246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 1247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentPositionMs -= (currentTimeMs - mSeekRequestTimeMs) 1248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * getPlaybackSpeed(); 1249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeShiftManager.this.onCurrentPositionChanged(); 1252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * The listener used to receive the events by the time-shift manager 1258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public interface Listener { 1260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the availability of the time-shift for the current channel has been changed. 1262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * If the time shift is available, {@link TimeShiftManager#getRecordStartTimeMs} should 1263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * return the valid time. 1264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onAvailabilityChanged(); 1266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1268816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the play status is changed between {@link #PLAY_STATUS_PLAYING} and 1269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link #PLAY_STATUS_PAUSED} 1270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 1271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param status The new play state. 1272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onPlayStatusChanged(int status); 1274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the recordStartTime has been changed. 1277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onRecordStartTimeChanged(); 1279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the current position is changed. 1282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onCurrentPositionChanged(); 1284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when the program information is updated. 1287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onProgramInfoChanged(); 1289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 1290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 1291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Called when an action becomes enabled or disabled. 1292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 1293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko void onActionEnabledChanged(@TimeShiftActionId int actionId, boolean enabled); 1294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 1295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko} 1296