165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko/* 265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Copyright (C) 2015 The Android Open Source Project 365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * you may not use this file except in compliance with the License. 665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * You may obtain a copy of the License at 765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 1065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Unless required by applicable law or agreed to in writing, software 1165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 1265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * See the License for the specific language governing permissions and 1465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * limitations under the License. 1565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 1665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkopackage com.android.tv.dvr.recorder; 1865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.annotation.TargetApi; 2065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.content.ContentUris; 2165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.media.tv.TvContract; 2265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.net.Uri; 2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Build; 2465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Message; 2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.MainThread; 2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.NonNull; 2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.Nullable; 2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.util.ArraySet; 2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.util.Log; 3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.ApplicationSingletons; 3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.InputSessionManager; 3365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.InputSessionManager.OnTvViewChannelChangeListener; 3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.MainActivity; 3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.TvApplication; 3665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.common.WeakHandler; 3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.data.Channel; 3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.data.ChannelDataManager; 3965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; 406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.DvrScheduleManager; 416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording; 426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.ui.DvrUiHelper; 4365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 4465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.ArrayList; 4565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.HashMap; 4665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.List; 4765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Map; 4865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Set; 4965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.concurrent.TimeUnit; 5065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 5165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko/** 5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Checking the runtime conflict of DVR recording. 5365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * <p> 5465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * This class runs only while the {@link MainActivity} is resumed and holds the upcoming conflicts. 5565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 5665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko@TargetApi(Build.VERSION_CODES.N) 5765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko@MainThread 5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic class ConflictChecker { 5965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final String TAG = "ConflictChecker"; 6065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final boolean DEBUG = false; 6165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 6265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final int MSG_CHECK_CONFLICT = 1; 6365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 6465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final long CHECK_RETRY_PERIOD_MS = TimeUnit.SECONDS.toMillis(30); 6565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 6665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 6765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * To show watch conflict dialog, the start time of the earliest conflicting schedule should be 6865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * less than or equal to this time. 6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final long MAX_WATCH_CONFLICT_CHECK_TIME_MS = TimeUnit.MINUTES.toMillis(5); 7165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 7265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * To show watch conflict dialog, the start time of the earliest conflicting schedule should be 7365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * greater than or equal to this time. 7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 7565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final long MIN_WATCH_CONFLICT_CHECK_TIME_MS = TimeUnit.SECONDS.toMillis(30); 7665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 7765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final MainActivity mMainActivity; 7865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final ChannelDataManager mChannelDataManager; 7965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final DvrScheduleManager mScheduleManager; 8065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final InputSessionManager mSessionManager; 8165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final ConflictCheckerHandler mHandler = new ConflictCheckerHandler(this); 8265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 8365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final List<ScheduledRecording> mUpcomingConflicts = new ArrayList<>(); 8465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Set<OnUpcomingConflictChangeListener> mOnUpcomingConflictChangeListeners = 8565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko new ArraySet<>(); 8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<Long, List<ScheduledRecording>> mCheckedConflictsMap = new HashMap<>(); 8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 8865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final ScheduledRecordingListener mScheduledRecordingListener = 8965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko new ScheduledRecordingListener() { 9065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 9165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { 9265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onScheduledRecordingAdded: " + scheduledRecordings); 9365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); 9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 9565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 9665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { 9865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onScheduledRecordingRemoved: " + scheduledRecordings); 9965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); 10065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 10165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 10265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 10365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) { 10465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onScheduledRecordingStatusChanged: " + scheduledRecordings); 10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); 10665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 10765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko }; 10865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 10965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final OnTvViewChannelChangeListener mOnTvViewChannelChangeListener = 11065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko new OnTvViewChannelChangeListener() { 11165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 11265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void onTvViewChannelChange(@Nullable Uri channelUri) { 11365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); 11465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 11565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko }; 11665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 11765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean mStarted; 11865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 11965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public ConflictChecker(MainActivity mainActivity) { 12065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mMainActivity = mainActivity; 12165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko ApplicationSingletons appSingletons = TvApplication.getSingletons(mainActivity); 12265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mChannelDataManager = appSingletons.getChannelDataManager(); 12365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduleManager = appSingletons.getDvrScheduleManager(); 12465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessionManager = appSingletons.getInputSessionManager(); 12565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 12665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 12765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 12865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Starts checking the conflict. 12965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 13065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void start() { 13165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mStarted) { 13265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 13365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStarted = true; 13565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_CHECK_CONFLICT); 13665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduleManager.addScheduledRecordingListener(mScheduledRecordingListener); 13765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessionManager.addOnTvViewChannelChangeListener(mOnTvViewChannelChangeListener); 13865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 14065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 14165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Stops checking the conflict. 14265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 14365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void stop() { 14465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!mStarted) { 14565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 14665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 14765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStarted = false; 14865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessionManager.removeOnTvViewChannelChangeListener(mOnTvViewChannelChangeListener); 14965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduleManager.removeScheduledRecordingListener(mScheduledRecordingListener); 15065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeCallbacksAndMessages(null); 15165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 15265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 15365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 15465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Returns the upcoming conflicts. 15565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 15665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public List<ScheduledRecording> getUpcomingConflicts() { 15765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return new ArrayList<>(mUpcomingConflicts); 15865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 15965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 16065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 16165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Adds a {@link OnUpcomingConflictChangeListener}. 16265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 16365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void addOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) { 16465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mOnUpcomingConflictChangeListeners.add(listener); 16565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 16665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 16765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 16865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Removes the {@link OnUpcomingConflictChangeListener}. 16965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 17065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void removeOnUpcomingConflictChangeListener(OnUpcomingConflictChangeListener listener) { 17165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mOnUpcomingConflictChangeListeners.remove(listener); 17265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 17465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void notifyUpcomingConflictChanged() { 17565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (OnUpcomingConflictChangeListener l : mOnUpcomingConflictChangeListeners) { 17665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko l.onUpcomingConflictChange(); 17765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 18165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Remembers the user's decision to record while watching the channel. 18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void setCheckedConflictsForChannel(long mChannelId, List<ScheduledRecording> conflicts) { 18465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCheckedConflictsMap.put(mChannelId, new ArrayList<>(conflicts)); 18565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 18665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 18765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void onCheckConflict() { 18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Checks the conflicting schedules and setup the next re-check time. 18965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // If there are upcoming conflicts soon, it opens the conflict dialog. 19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "Handling MSG_CHECK_CONFLICT"); 19165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_CHECK_CONFLICT); 19265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mUpcomingConflicts.clear(); 19365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!mScheduleManager.isInitialized() 19465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko || !mChannelDataManager.isDbLoadFinished()) { 19565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, CHECK_RETRY_PERIOD_MS); 19665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko notifyUpcomingConflictChanged(); 19765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 19865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 19965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mSessionManager.getCurrentTvViewChannelUri() == null) { 20065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // As MainActivity is not using a tuner, no need to check the conflict. 20165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko notifyUpcomingConflictChanged(); 20265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Uri channelUri = mSessionManager.getCurrentTvViewChannelUri(); 20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (TvContract.isChannelUriForPassthroughInput(channelUri)) { 20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko notifyUpcomingConflictChanged(); 20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 20865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long channelId = ContentUris.parseId(channelUri); 21065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Channel channel = mChannelDataManager.getChannel(channelId); 21165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The conflicts caused by watching the channel. 21265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko List<ScheduledRecording> conflicts = mScheduleManager 21365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko .getConflictingSchedulesForWatching(channel.getId()); 21465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long earliestToCheck = Long.MAX_VALUE; 21565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long currentTimeMs = System.currentTimeMillis(); 21665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (ScheduledRecording schedule : conflicts) { 21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long startTimeMs = schedule.getStartTimeMs(); 21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (startTimeMs < currentTimeMs + MIN_WATCH_CONFLICT_CHECK_TIME_MS) { 21965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The start time of the upcoming conflict remains less than the minimum 22065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // check time. 22165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko continue; 22265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (startTimeMs > currentTimeMs + MAX_WATCH_CONFLICT_CHECK_TIME_MS) { 22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The start time of the upcoming conflict remains greater than the 22565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // maximum check time. Setup the next re-check time. 22665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long nextCheckTimeMs = startTimeMs - MAX_WATCH_CONFLICT_CHECK_TIME_MS; 22765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (earliestToCheck > nextCheckTimeMs) { 22865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko earliestToCheck = nextCheckTimeMs; 22965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 23165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Found upcoming conflicts which will start soon. 23265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mUpcomingConflicts.add(schedule); 23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The schedule will be removed from the "upcoming conflict" when the 23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // recording is almost started. 23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long nextCheckTimeMs = startTimeMs - MIN_WATCH_CONFLICT_CHECK_TIME_MS; 23665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (earliestToCheck > nextCheckTimeMs) { 23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko earliestToCheck = nextCheckTimeMs; 23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (earliestToCheck != Long.MAX_VALUE) { 24265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_CHECK_CONFLICT, 24365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko earliestToCheck - currentTimeMs); 24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "upcoming conflicts: " + mUpcomingConflicts); 24665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko notifyUpcomingConflictChanged(); 24765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!mUpcomingConflicts.isEmpty() 24865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko && !DvrUiHelper.isChannelWatchConflictDialogShown(mMainActivity)) { 24965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Don't show the conflict dialog if the user already knows. 25065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko List<ScheduledRecording> checkedConflicts = mCheckedConflictsMap.get( 25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko channel.getId()); 25265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (checkedConflicts == null 25365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko || !checkedConflicts.containsAll(mUpcomingConflicts)) { 25465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko DvrUiHelper.showChannelWatchConflictDialog(mMainActivity, channel); 25565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 25965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static class ConflictCheckerHandler extends WeakHandler<ConflictChecker> { 26065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko ConflictCheckerHandler(ConflictChecker conflictChecker) { 26165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko super(conflictChecker); 26265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 26365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 26465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 26565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko protected void handleMessage(Message msg, @NonNull ConflictChecker conflictChecker) { 26665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko switch (msg.what) { 26765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_CHECK_CONFLICT: 26865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko conflictChecker.onCheckConflict(); 26965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 27065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 27465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 27565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * A listener for the change of upcoming conflicts. 27665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 27765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public interface OnUpcomingConflictChangeListener { 27865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void onUpcomingConflictChange(); 27965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko} 281