11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
171abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopackage com.android.tv.dvr;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.annotation.TargetApi;
20d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.ContentProviderOperation;
212e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.content.ContentResolver;
2265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.content.ContentUris;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context;
24d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.OperationApplicationException;
2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.media.tv.TvContract;
262e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.media.tv.TvInputInfo;
2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.net.Uri;
28d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.AsyncTask;
2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Build;
302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.os.Handler;
31d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.RemoteException;
32ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread;
33ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.NonNull;
3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.Nullable;
3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.VisibleForTesting;
362e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.support.annotation.WorkerThread;
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
38d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.util.Range;
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
411abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.TvApplication;
422e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.common.SoftPreconditions;
43ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.feature.CommonFeatures;
441abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.data.Channel;
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.data.Program;
46d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.dvr.DvrDataManager.OnRecordedProgramLoadFinishedListener;
47d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.dvr.DvrDataManager.RecordedProgramListener;
48d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.dvr.DvrScheduleManager.OnInitializeListener;
496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.RecordedProgram;
506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording;
516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.SeriesRecording;
522e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.util.AsyncDbTask;
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.util.Utils;
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
5565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.io.File;
5665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.ArrayList;
5765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Arrays;
58ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport java.util.Collections;
592e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport java.util.HashMap;
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
612e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport java.util.Map;
622e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport java.util.Map.Entry;
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * DVR manager class to add and remove recordings. UI can modify recording list through this class,
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * instead of modifying them directly through {@link DvrDataManager}.
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
68ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko@MainThread
6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko@TargetApi(Build.VERSION_CODES.N)
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class DvrManager {
7165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static final String TAG = "DvrManager";
7265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static final boolean DEBUG = false;
7365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final WritableDvrDataManager mDataManager;
7565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final DvrScheduleManager mScheduleManager;
762e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    // @GuardedBy("mListener")
772e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final Map<Listener, Handler> mListener = new HashMap<>();
782e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final Context mAppContext;
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public DvrManager(Context context) {
81ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG);
82d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mAppContext = context.getApplicationContext();
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mDataManager = (WritableDvrDataManager) appSingletons.getDvrDataManager();
8565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mScheduleManager = appSingletons.getDvrScheduleManager();
86d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) {
87d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            createSeriesRecordingsForRecordedProgramsIfNeeded(mDataManager.getRecordedPrograms());
88d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        } else {
89d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // No need to handle DVR schedule load finished because schedule manager is initialized
90d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // after the all the schedules are loaded.
91d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (!mDataManager.isRecordedProgramLoadFinished()) {
92d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                mDataManager.addRecordedProgramLoadFinishedListener(
93d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        new OnRecordedProgramLoadFinishedListener() {
94d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            @Override
95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            public void onRecordedProgramLoadFinished() {
96d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                mDataManager.removeRecordedProgramLoadFinishedListener(this);
97d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                if (mDataManager.isInitialized()
98d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                        && mScheduleManager.isInitialized()) {
99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                    createSeriesRecordingsForRecordedProgramsIfNeeded(
100d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                            mDataManager.getRecordedPrograms());
101d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                }
102d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            }
103d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        });
104d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
105d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (!mScheduleManager.isInitialized()) {
106d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                mScheduleManager.addOnInitializeListener(new OnInitializeListener() {
107d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    @Override
108d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    public void onInitialize() {
109d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        mScheduleManager.removeOnInitializeListener(this);
110d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        if (mDataManager.isInitialized() && mScheduleManager.isInitialized()) {
111d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            createSeriesRecordingsForRecordedProgramsIfNeeded(
112d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                    mDataManager.getRecordedPrograms());
113d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        }
114d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    }
115d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                });
116d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
117d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
118d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mDataManager.addRecordedProgramListener(new RecordedProgramListener() {
119d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            @Override
120d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
121d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                if (!mDataManager.isInitialized() || !mScheduleManager.isInitialized()) {
122d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    return;
123d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                }
124d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                for (RecordedProgram recordedProgram : recordedPrograms) {
125d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram);
126d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                }
127d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
128d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
129d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            @Override
130d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { }
131d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
132d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            @Override
133d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
134d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                // Removing series recording is handled in the SeriesRecordingDetailsFragment.
135d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
136d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        });
137d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
138d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
139d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void createSeriesRecordingsForRecordedProgramsIfNeeded(
140d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            List<RecordedProgram> recordedPrograms) {
141d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        for (RecordedProgram recordedProgram : recordedPrograms) {
142d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            createSeriesRecordingForRecordedProgramIfNeeded(recordedProgram);
143d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
144d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
145d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
146d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void createSeriesRecordingForRecordedProgramIfNeeded(RecordedProgram recordedProgram) {
1476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (recordedProgram.isEpisodic()) {
148d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            SeriesRecording seriesRecording =
149d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    mDataManager.getSeriesRecording(recordedProgram.getSeriesId());
150d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (seriesRecording == null) {
151d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                addSeriesRecording(recordedProgram);
152d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
153d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
15465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
15565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
15665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
15765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Schedules a recording for {@code program}.
15865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
159d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public ScheduledRecording addSchedule(Program program) {
16065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
161d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return null;
16265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
16365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        SeriesRecording seriesRecording = getSeriesRecording(program);
164d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return addSchedule(program, seriesRecording == null
165d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                ? mScheduleManager.suggestNewPriority()
166d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                : seriesRecording.getPriority());
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
170d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Schedules a recording for {@code program} with the highest priority so that the schedule
171d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * can be recorded.
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
173d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public ScheduledRecording addScheduleWithHighestPriority(Program program) {
17465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
175d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return null;
17665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
177d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        SeriesRecording seriesRecording = getSeriesRecording(program);
178d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return addSchedule(program, seriesRecording == null
179d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                ? mScheduleManager.suggestNewPriority()
180d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                : mScheduleManager.suggestHighestPriority(seriesRecording.getInputId(),
181d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        new Range(program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()),
182d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        seriesRecording.getPriority()));
183d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
184d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
185d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private ScheduledRecording addSchedule(Program program, long priority) {
18665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TvInputInfo input = Utils.getTvInputInfoForProgram(mAppContext, program);
18765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (input == null) {
18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.e(TAG, "Can't find input for program: " + program);
189d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return null;
19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
191d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        ScheduledRecording schedule;
192d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        SeriesRecording seriesRecording = getSeriesRecording(program);
193d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        schedule = createScheduledRecordingBuilder(input.getId(), program)
194ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                .setPriority(priority)
195d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                .setSeriesRecordingId(seriesRecording == null ? SeriesRecording.ID_NOT_SET
196d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        : seriesRecording.getId())
197ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                .build();
198d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mDataManager.addScheduledRecording(schedule);
199d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return schedule;
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Adds a recording schedule with a time range.
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void addSchedule(Channel channel, long startTime, long endTime) {
20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Log.i(TAG, "Adding scheduled recording of channel " + channel + " starting at " +
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Utils.toTimeString(startTime) + " and ending at " + Utils.toTimeString(endTime));
20865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
20965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
21065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
21165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TvInputInfo input = Utils.getTvInputInfoForChannelId(mAppContext, channel.getId());
21265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (input == null) {
21365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.e(TAG, "Can't find input for channel: " + channel);
21465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
21565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
21665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        addScheduleInternal(input.getId(), channel.getId(), startTime, endTime);
21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
219d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
220d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Adds the schedule.
221d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
222d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void addSchedule(ScheduledRecording schedule) {
223d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (mDataManager.isDvrScheduleLoadFinished()) {
224d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mDataManager.addScheduledRecording(schedule);
225d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
226d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
227d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
22865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private void addScheduleInternal(String inputId, long channelId, long startTime, long endTime) {
22965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mDataManager.addScheduledRecording(ScheduledRecording
23065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .builder(inputId, channelId, startTime, endTime)
23165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .setPriority(mScheduleManager.suggestNewPriority())
23265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .build());
23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
236d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Adds a new series recording and schedules for the programs with the initial state.
23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public SeriesRecording addSeriesRecording(Program selectedProgram,
2396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            List<Program> programsToSchedule, @SeriesRecording.SeriesState int initialState) {
24065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Log.i(TAG, "Adding series recording for program " + selectedProgram + ", and schedules: "
24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                + programsToSchedule);
24265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
24365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TvInputInfo input = Utils.getTvInputInfoForProgram(mAppContext, selectedProgram);
24665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (input == null) {
24765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.e(TAG, "Can't find input for program: " + selectedProgram);
24865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
24965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
25065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        SeriesRecording seriesRecording = SeriesRecording.builder(input.getId(), selectedProgram)
25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .setPriority(mScheduleManager.suggestNewSeriesPriority())
252d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                .setState(initialState)
2532e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                .build();
25465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mDataManager.addSeriesRecording(seriesRecording);
25565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        // The schedules for the recorded programs should be added not to create the schedule the
25665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        // duplicate episodes.
25765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        addRecordedProgramToSeriesRecording(seriesRecording);
25865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        addScheduleToSeriesRecording(seriesRecording, programsToSchedule);
25965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return seriesRecording;
26065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
26165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
262d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void addSeriesRecording(RecordedProgram recordedProgram) {
263d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        SeriesRecording seriesRecording =
264d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                SeriesRecording.builder(recordedProgram.getInputId(), recordedProgram)
265d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .setPriority(mScheduleManager.suggestNewSeriesPriority())
266d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .setState(SeriesRecording.STATE_SERIES_STOPPED)
267d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .build();
268d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        mDataManager.addSeriesRecording(seriesRecording);
269d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        // The schedules for the recorded programs should be added not to create the schedule the
270d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        // duplicate episodes.
271d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        addRecordedProgramToSeriesRecording(seriesRecording);
272d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
273d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
27465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private void addRecordedProgramToSeriesRecording(SeriesRecording series) {
27565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        List<ScheduledRecording> toAdd = new ArrayList<>();
27665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (RecordedProgram recordedProgram : mDataManager.getRecordedPrograms()) {
27765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (series.getSeriesId().equals(recordedProgram.getSeriesId())
27865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && !recordedProgram.isClipped()) {
27965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                // Duplicate schedules can exist, but they will be deleted in a few days. And it's
28065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                // also guaranteed that the schedules don't belong to any series recordings because
28165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                // there are no more than one series recordings which have the same program title.
28265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                toAdd.add(ScheduledRecording.builder(recordedProgram)
28365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        .setPriority(series.getPriority())
28465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        .setSeriesRecordingId(series.getId()).build());
28565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
28665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
28765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!toAdd.isEmpty()) {
28865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mDataManager.addScheduledRecording(ScheduledRecording.toArray(toAdd));
28965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
29065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
29165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
29265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
29365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Adds {@link ScheduledRecording}s for the series recording.
29465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>
29565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * This method doesn't add the series recording.
29665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
29765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void addScheduleToSeriesRecording(SeriesRecording series,
29865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            List<Program> programsToSchedule) {
29965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
30065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
30165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
30265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TvInputInfo input = Utils.getTvInputInfoForInputId(mAppContext, series.getInputId());
30365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (input == null) {
30465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.e(TAG, "Can't find input with ID: " + series.getInputId());
30565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
30665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
30765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        List<ScheduledRecording> toAdd = new ArrayList<>();
30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        List<ScheduledRecording> toUpdate = new ArrayList<>();
30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (Program program : programsToSchedule) {
31065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            ScheduledRecording scheduleWithSameProgram =
31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mDataManager.getScheduledRecordingForProgramId(program.getId());
31265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (scheduleWithSameProgram != null) {
3136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (scheduleWithSameProgram.isNotStarted()) {
31465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    ScheduledRecording r = ScheduledRecording.buildFrom(scheduleWithSameProgram)
31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            .setSeriesRecordingId(series.getId())
31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            .build();
31765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    if (!r.equals(scheduleWithSameProgram)) {
31865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        toUpdate.add(r);
31965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
32065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
32165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            } else {
322d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                toAdd.add(createScheduledRecordingBuilder(input.getId(), program)
32365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        .setPriority(series.getPriority())
324d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .setSeriesRecordingId(series.getId())
325d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .build());
32665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
32765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
32865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!toAdd.isEmpty()) {
32965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mDataManager.addScheduledRecording(ScheduledRecording.toArray(toAdd));
33065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
33165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!toUpdate.isEmpty()) {
33265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mDataManager.updateScheduledRecording(ScheduledRecording.toArray(toUpdate));
33365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
33465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
33665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
33765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Updates the series recording.
33865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
33965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void updateSeriesRecording(SeriesRecording series) {
34065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
34165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            SeriesRecording previousSeries = mDataManager.getSeriesRecording(series.getId());
34265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (previousSeries != null) {
3436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                // If the channel option of series changed, remove the existing schedules. The new
3446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                // schedules will be added by SeriesRecordingScheduler or by SeriesSettingsFragment.
345d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                if (previousSeries.getChannelOption() != series.getChannelOption()
346d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        || (previousSeries.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE
347d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        && previousSeries.getChannelId() != series.getChannelId())) {
348d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    List<ScheduledRecording> schedules =
349d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            mDataManager.getScheduledRecordings(series.getId());
350d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    List<ScheduledRecording> schedulesToRemove = new ArrayList<>();
351d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    for (ScheduledRecording schedule : schedules) {
352d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        if (schedule.isNotStarted()) {
353d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            schedulesToRemove.add(schedule);
3546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        } else if (schedule.isInProgress()
3556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                && series.getChannelOption() == SeriesRecording.OPTION_CHANNEL_ONE
3566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                && schedule.getChannelId() != series.getChannelId()) {
3576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            stopRecording(schedule);
3586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        }
3596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    }
3606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    List<ScheduledRecording> deletedSchedules =
3616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            new ArrayList<>(mDataManager.getDeletedSchedules());
3626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    for (ScheduledRecording deletedSchedule : deletedSchedules) {
3636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        if (deletedSchedule.getSeriesRecordingId() == series.getId()
3646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                                && deletedSchedule.getEndTimeMs() > System.currentTimeMillis()) {
3656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                            schedulesToRemove.add(deletedSchedule);
366d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        }
367d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    }
368d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    mDataManager.removeScheduledRecording(true,
369d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            ScheduledRecording.toArray(schedulesToRemove));
370d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                }
37165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
37265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mDataManager.updateSeriesRecording(series);
373d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (previousSeries == null
374d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    || previousSeries.getPriority() != series.getPriority()) {
37565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                long priority = series.getPriority();
37665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                List<ScheduledRecording> schedulesToUpdate = new ArrayList<>();
37765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                for (ScheduledRecording schedule
37865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        : mDataManager.getScheduledRecordings(series.getId())) {
3796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    if (schedule.isNotStarted() || schedule.isInProgress()) {
38065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        schedulesToUpdate.add(ScheduledRecording.buildFrom(schedule)
38165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                                .setPriority(priority).build());
38265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
38365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
38465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (!schedulesToUpdate.isEmpty()) {
38565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mDataManager.updateScheduledRecording(
38665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            ScheduledRecording.toArray(schedulesToUpdate));
38765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
38865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
38965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
39065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
39165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
39265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
39365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Removes the series recording and all the corresponding schedules which are not started yet.
39465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
39565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void removeSeriesRecording(long seriesRecordingId) {
39665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
39765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
39865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
39965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        SeriesRecording series = mDataManager.getSeriesRecording(seriesRecordingId);
40065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (series == null) {
40165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
40265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
40365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (ScheduledRecording schedule : mDataManager.getAllScheduledRecordings()) {
40465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (schedule.getSeriesRecordingId() == seriesRecordingId) {
40565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
40665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    stopRecording(schedule);
40765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    break;
40865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
40965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
41065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
41165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mDataManager.removeSeriesRecording(series);
4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
4152e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     * Stops the currently recorded program
4162e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     */
4172e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    public void stopRecording(final ScheduledRecording recording) {
41865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
41965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
42065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
4212e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        synchronized (mListener) {
4222e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            for (final Entry<Listener, Handler> entry : mListener.entrySet()) {
4232e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                entry.getValue().post(new Runnable() {
4242e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    @Override
4252e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    public void run() {
4262e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                        entry.getKey().onStopRecordingRequested(recording);
4272e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    }
4282e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                });
4292e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
4302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
4312e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
4322e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
4332e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    /**
43465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Removes scheduled recordings or an existing recordings.
4351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
43665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void removeScheduledRecording(ScheduledRecording... schedules) {
43765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Log.i(TAG, "Removing " + Arrays.asList(schedules));
43865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
43965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
44065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
44165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (ScheduledRecording r : schedules) {
44265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
44365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                stopRecording(r);
44465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            } else {
44565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mDataManager.removeScheduledRecording(r);
44665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
44765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
44865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
44965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
45065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
451d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Removes scheduled recordings without changing to the DELETED state.
452d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
453d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void forceRemoveScheduledRecording(ScheduledRecording... schedules) {
454d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        Log.i(TAG, "Force removing " + Arrays.asList(schedules));
455d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
456d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return;
457d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
458d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        for (ScheduledRecording r : schedules) {
459d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (r.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
460d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                stopRecording(r);
461d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            } else {
462d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                mDataManager.removeScheduledRecording(true, r);
463d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
464d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
465d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
466d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
467d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
46865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Removes the recorded program. It deletes the file if possible.
46965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
47065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void removeRecordedProgram(Uri recordedProgramUri) {
47165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
47265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
47365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
47465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        removeRecordedProgram(ContentUris.parseId(recordedProgramUri));
47565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
47665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
47765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
47865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Removes the recorded program. It deletes the file if possible.
47965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
48065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void removeRecordedProgram(long recordedProgramId) {
48165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
48265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
48365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
48465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        RecordedProgram recordedProgram = mDataManager.getRecordedProgram(recordedProgramId);
48565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (recordedProgram != null) {
48665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            removeRecordedProgram(recordedProgram);
48765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
4882e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
4892e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
49065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
49165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Removes the recorded program. It deletes the file if possible.
49265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
4932e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    public void removeRecordedProgram(final RecordedProgram recordedProgram) {
49465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized())) {
49565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return;
49665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
4976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        new AsyncDbTask<Void, Void, Integer>() {
4982e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            @Override
4996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            protected Integer doInBackground(Void... params) {
5002e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                ContentResolver resolver = mAppContext.getContentResolver();
5016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                return resolver.delete(recordedProgram.getUri(), null, null);
5026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
5036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
5046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
5056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            protected void onPostExecute(Integer deletedCounts) {
506d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                if (deletedCounts > 0) {
507d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    new AsyncTask<Void, Void, Void>() {
508d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        @Override
509d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        protected Void doInBackground(Void... params) {
510d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            removeRecordedData(recordedProgram.getDataUri());
511d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            return null;
51265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        }
513d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
51465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
5152e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
51665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }.executeOnDbThread();
5171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
5181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
519d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void removeRecordedPrograms(List<Long> recordedProgramIds) {
520d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        final ArrayList<ContentProviderOperation> dbOperations = new ArrayList<>();
521d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        final List<Uri> dataUris = new ArrayList<>();
522d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        for (Long rId : recordedProgramIds) {
523d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            RecordedProgram r = mDataManager.getRecordedProgram(rId);
524d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (r != null) {
525d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                dataUris.add(r.getDataUri());
526d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                dbOperations.add(ContentProviderOperation.newDelete(r.getUri()).build());
527d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
52865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
5296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        new AsyncDbTask<Void, Void, Boolean>() {
53065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            @Override
5316ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            protected Boolean doInBackground(Void... params) {
53265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                ContentResolver resolver = mAppContext.getContentResolver();
533d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                try {
534d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    resolver.applyBatch(TvContract.AUTHORITY, dbOperations);
5356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                } catch (RemoteException | OperationApplicationException e) {
5366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    Log.w(TAG, "Remove recorded programs from DB failed.", e);
5376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    return false;
5386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
5396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                return true;
5406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
5416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
5426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
5436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            protected void onPostExecute(Boolean success) {
5446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (success) {
545d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    new AsyncTask<Void, Void, Void>() {
546d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        @Override
547d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        protected Void doInBackground(Void... params) {
548d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            for (Uri dataUri : dataUris) {
549d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                                removeRecordedData(dataUri);
550d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            }
551d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                            return null;
552d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        }
553d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
554d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                }
55565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
55665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }.executeOnDbThread();
55765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
55865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
55965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
56065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Updates the scheduled recording.
56165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
56265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void updateScheduledRecording(ScheduledRecording recording) {
56365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
56465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mDataManager.updateScheduledRecording(recording);
56565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
56665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
56765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
56865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
56965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns priority ordered list of all scheduled recordings that will not be recorded if
570ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     * this program is.
571ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     *
57265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @see DvrScheduleManager#getConflictingSchedules(Program)
57365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
57465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public List<ScheduledRecording> getConflictingSchedules(Program program) {
57565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
57665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return Collections.emptyList();
57765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
57865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mScheduleManager.getConflictingSchedules(program);
57965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
58065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
58165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
58265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns priority ordered list of all scheduled recordings that will not be recorded if
58365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * this channel is.
58465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *
58565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @see DvrScheduleManager#getConflictingSchedules(long, long, long)
58665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
58765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs,
58865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            long endTimeMs) {
58965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
59065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return Collections.emptyList();
59165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
59265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mScheduleManager.getConflictingSchedules(channelId, startTimeMs, endTimeMs);
59365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
59465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
59565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
59665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Checks if the schedule is conflicting.
59765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *
59865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>Note that the {@code schedule} should be the existing one. If not, this returns
59965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * {@code false}.
60065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
60165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public boolean isConflicting(ScheduledRecording schedule) {
602d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return schedule != null
603d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                && SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())
604d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                && mScheduleManager.isConflicting(schedule);
60565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
60665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
60765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
60865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns priority ordered list of all scheduled recording that will not be recorded if
60965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * this channel is tuned to.
61065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     *
61165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * @see DvrScheduleManager#getConflictingSchedulesForTune
61265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
61365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public List<ScheduledRecording> getConflictingSchedulesForTune(long channelId) {
61465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
61565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return Collections.emptyList();
61665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
61765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mScheduleManager.getConflictingSchedulesForTune(channelId);
61865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
61965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
62065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
621d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Sets the highest priority to the schedule.
62265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
623d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void setHighestPriority(ScheduledRecording schedule) {
624d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
625d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            long newPriority = mScheduleManager.suggestHighestPriority(schedule);
626d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (newPriority != schedule.getPriority()) {
627d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule)
628d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        .setPriority(newPriority).build());
629ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
630ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
631d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
632d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
633d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
634d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Suggests the higher priority than the schedules which overlap with {@code schedule}.
635d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
636d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public long suggestHighestPriority(ScheduledRecording schedule) {
637d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
638d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return mScheduleManager.suggestHighestPriority(schedule);
639d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
640d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return DvrScheduleManager.DEFAULT_PRIORITY;
641ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
642ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
64365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
64465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns {@code true} if the channel can be recorded.
64565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>
64665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Note that this method doesn't check the conflict of the schedule or available tuners.
64765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * This can be called from the UI before the schedules are loaded.
64865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
64965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public boolean isChannelRecordable(Channel channel) {
65065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!mDataManager.isDvrScheduleLoadFinished() || channel == null) {
65165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
65265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
6536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (channel.isRecordingProhibited()) {
6546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return false;
6556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
65665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        TvInputInfo info = Utils.getTvInputInfoForChannelId(mAppContext, channel.getId());
65765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (info == null) {
65865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.w(TAG, "Could not find TvInputInfo for " + channel);
65965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
66065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
66165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!info.canRecord()) {
66265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
66365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
66465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Program program = TvApplication.getSingletons(mAppContext).getProgramDataManager()
66565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .getCurrentProgram(channel.getId());
66665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return program == null || !program.isRecordingProhibited();
6671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
67065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns {@code true} if the program can be recorded.
67165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>
67265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Note that this method doesn't check the conflict of the schedule or available tuners.
67365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * This can be called from the UI before the schedules are loaded.
6741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
67565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public boolean isProgramRecordable(Program program) {
67665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!mDataManager.isInitialized()) {
67765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
67865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
6796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Channel channel = TvApplication.getSingletons(mAppContext).getChannelDataManager()
6806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                .getChannel(program.getChannelId());
6816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (channel == null || channel.isRecordingProhibited()) {
6826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            return false;
6836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
6846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        TvInputInfo info = Utils.getTvInputInfoForChannelId(mAppContext, channel.getId());
68565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (info == null) {
68665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Log.w(TAG, "Could not find TvInputInfo for " + program);
68765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return false;
68865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
68965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return info.canRecord() && !program.isRecordingProhibited();
6901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
691ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
692ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    /**
69365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns the current recording for the channel.
69465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * <p>
69565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * This can be called from the UI before the schedules are loaded.
696ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko     */
69765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public ScheduledRecording getCurrentRecording(long channelId) {
69865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!mDataManager.isDvrScheduleLoadFinished()) {
69965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
70065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
70165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (ScheduledRecording recording : mDataManager.getStartedRecordings()) {
70265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (recording.getChannelId() == channelId) {
70365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return recording;
70465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
70565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
70665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return null;
70765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
70865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
70965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
710d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Returns schedules which is available (i.e., isNotStarted or isInProgress) and belongs to
711d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * the series recording {@code seriesRecordingId}.
712d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
713d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public List<ScheduledRecording> getAvailableScheduledRecording(long seriesRecordingId) {
714d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (!mDataManager.isDvrScheduleLoadFinished()) {
715d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return Collections.emptyList();
716d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
717d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        List<ScheduledRecording> schedules = new ArrayList<>();
718d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        for (ScheduledRecording schedule : mDataManager.getScheduledRecordings(seriesRecordingId)) {
719d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (schedule.isInProgress() || schedule.isNotStarted()) {
720d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                schedules.add(schedule);
721d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
722d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
723d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        return schedules;
724d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
725d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
726d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
72765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns the series recording related to the program.
72865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
72965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    @Nullable
73065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public SeriesRecording getSeriesRecording(Program program) {
73165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) {
73265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
73365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
73465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return mDataManager.getSeriesRecording(program.getSeriesId());
7352e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
7362e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
7376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
7386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Returns if there are valid items. Valid item contains {@link RecordedProgram},
7396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * available {@link ScheduledRecording} and {@link SeriesRecording}.
7406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
7416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public boolean hasValidItems() {
7426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        return !(mDataManager.getRecordedPrograms().isEmpty()
7436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                && mDataManager.getStartedRecordings().isEmpty()
7446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                && mDataManager.getNonStartedScheduledRecordings().isEmpty()
7456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                && mDataManager.getSeriesRecordings().isEmpty());
7466ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
7476ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
7482e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    @WorkerThread
74965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    @VisibleForTesting
75065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    // Should be public to use mock DvrManager object.
75165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void addListener(Listener listener, @NonNull Handler handler) {
7522e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        SoftPreconditions.checkNotNull(handler);
7532e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        synchronized (mListener) {
7542e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            mListener.put(listener, handler);
7552e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
7562e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
7572e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
7582e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    @WorkerThread
75965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    @VisibleForTesting
76065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    // Should be public to use mock DvrManager object.
76165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public void removeListener(Listener listener) {
7622e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        synchronized (mListener) {
7632e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            mListener.remove(listener);
7642e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
7652e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
7662e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
7672e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    /**
76865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns ScheduledRecording.builder based on {@code program}. If program is already started,
76965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * recording started time is clipped to the current time.
77065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
77165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private ScheduledRecording.Builder createScheduledRecordingBuilder(String inputId,
77265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            Program program) {
77365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ScheduledRecording.Builder builder = ScheduledRecording.builder(inputId, program);
77465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        long time = System.currentTimeMillis();
77565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (program.getStartTimeUtcMillis() < time && time < program.getEndTimeUtcMillis()) {
77665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            builder.setStartTimeMs(time);
77765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
77865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return builder;
77965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
78065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
78165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
78265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns a schedule which matches to the given episode.
78365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
78465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public ScheduledRecording getScheduledRecording(String title, String seasonNumber,
78565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            String episodeNumber) {
78665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null
78765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                || seasonNumber == null || episodeNumber == null) {
78865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
78965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
79065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (ScheduledRecording r : mDataManager.getAllScheduledRecordings()) {
79165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (title.equals(r.getProgramTitle())
79265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && seasonNumber.equals(r.getSeasonNumber())
79365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && episodeNumber.equals(r.getEpisodeNumber())) {
79465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return r;
79565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
79665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
79765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return null;
79865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
79965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
80065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
80165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     * Returns a recorded program which is the same episode as the given {@code program}.
80265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko     */
80365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    public RecordedProgram getRecordedProgram(String title, String seasonNumber,
80465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            String episodeNumber) {
80565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        if (!SoftPreconditions.checkState(mDataManager.isInitialized()) || title == null
80665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                || seasonNumber == null || episodeNumber == null) {
80765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            return null;
80865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
80965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (RecordedProgram r : mDataManager.getRecordedPrograms()) {
81065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (title.equals(r.getTitle())
81165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && seasonNumber.equals(r.getSeasonNumber())
81265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && episodeNumber.equals(r.getEpisodeNumber())
81365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    && !r.isClipped()) {
81465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return r;
81565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
81665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
81765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        return null;
81865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
81965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
820d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    @WorkerThread
821d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void removeRecordedData(Uri dataUri) {
822d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        try {
823d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (dataUri != null && ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())
824d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    && dataUri.getPath() != null) {
825d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                File recordedProgramPath = new File(dataUri.getPath());
826d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                if (!recordedProgramPath.exists()) {
827d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    if (DEBUG) Log.d(TAG, "File to delete not exist: " + recordedProgramPath);
828d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                } else {
829d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    Utils.deleteDirOrFile(recordedProgramPath);
830d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    if (DEBUG) {
831d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        Log.d(TAG, "Sucessfully deleted files of the recorded program: " + dataUri);
832d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    }
833d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                }
834d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
835d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        } catch (SecurityException e) {
836d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            if (DEBUG) {
837d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                Log.d(TAG, "To delete this recorded program, please manually delete video data at"
838d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                        + "\nadb shell rm -rf " + dataUri);
839d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            }
840d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
841d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
842d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
843d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    /**
844d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Remove all the records related to the input.
845d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * <p>
846d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     * Note that this should be called after the input was removed.
847d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko     */
848d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    public void forgetStorage(String inputId) {
849d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (mDataManager.isInitialized()) {
850d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            mDataManager.forgetStorage(inputId);
851d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
852d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    }
853d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko
85465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    /**
8556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Listener to stop recording request. Should only be internally used inside dvr and its
8566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * sub-package.
8572e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko     */
8586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    public interface Listener {
8592e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        void onStopRecordingRequested(ScheduledRecording scheduledRecording);
860ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
8611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
862