BaseDvrDataManager.java revision 944779887775bd950cf1abf348d2df461593f6ab
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.tv.dvr; 18 19import android.annotation.TargetApi; 20import android.content.Context; 21import android.os.Build; 22import android.support.annotation.MainThread; 23import android.support.annotation.NonNull; 24import android.util.ArraySet; 25import android.util.Log; 26import com.android.tv.common.SoftPreconditions; 27import com.android.tv.common.feature.CommonFeatures; 28import com.android.tv.common.util.Clock; 29import com.android.tv.dvr.data.RecordedProgram; 30import com.android.tv.dvr.data.ScheduledRecording; 31import com.android.tv.dvr.data.ScheduledRecording.RecordingState; 32import com.android.tv.dvr.data.SeriesRecording; 33import java.util.ArrayList; 34import java.util.Arrays; 35import java.util.Collection; 36import java.util.Collections; 37import java.util.HashMap; 38import java.util.List; 39import java.util.Map; 40import java.util.Set; 41import java.util.concurrent.CopyOnWriteArraySet; 42 43/** Base implementation of @{link DataManagerInternal}. */ 44@MainThread 45@TargetApi(Build.VERSION_CODES.N) 46public abstract class BaseDvrDataManager implements WritableDvrDataManager { 47 private static final String TAG = "BaseDvrDataManager"; 48 private static final boolean DEBUG = false; 49 protected final Clock mClock; 50 51 private final Set<OnDvrScheduleLoadFinishedListener> mOnDvrScheduleLoadFinishedListeners = 52 new CopyOnWriteArraySet<>(); 53 private final Set<OnRecordedProgramLoadFinishedListener> 54 mOnRecordedProgramLoadFinishedListeners = new CopyOnWriteArraySet<>(); 55 private final Set<ScheduledRecordingListener> mScheduledRecordingListeners = new ArraySet<>(); 56 private final Set<SeriesRecordingListener> mSeriesRecordingListeners = new ArraySet<>(); 57 private final Set<RecordedProgramListener> mRecordedProgramListeners = new ArraySet<>(); 58 private final HashMap<Long, ScheduledRecording> mDeletedScheduleMap = new HashMap<>(); 59 60 BaseDvrDataManager(Context context, Clock clock) { 61 SoftPreconditions.checkFeatureEnabled(context, CommonFeatures.DVR, TAG); 62 mClock = clock; 63 } 64 65 @Override 66 public void addDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener) { 67 mOnDvrScheduleLoadFinishedListeners.add(listener); 68 } 69 70 @Override 71 public void removeDvrScheduleLoadFinishedListener(OnDvrScheduleLoadFinishedListener listener) { 72 mOnDvrScheduleLoadFinishedListeners.remove(listener); 73 } 74 75 @Override 76 public void addRecordedProgramLoadFinishedListener( 77 OnRecordedProgramLoadFinishedListener listener) { 78 mOnRecordedProgramLoadFinishedListeners.add(listener); 79 } 80 81 @Override 82 public void removeRecordedProgramLoadFinishedListener( 83 OnRecordedProgramLoadFinishedListener listener) { 84 mOnRecordedProgramLoadFinishedListeners.remove(listener); 85 } 86 87 @Override 88 public final void addScheduledRecordingListener(ScheduledRecordingListener listener) { 89 mScheduledRecordingListeners.add(listener); 90 } 91 92 @Override 93 public final void removeScheduledRecordingListener(ScheduledRecordingListener listener) { 94 mScheduledRecordingListeners.remove(listener); 95 } 96 97 @Override 98 public final void addSeriesRecordingListener(SeriesRecordingListener listener) { 99 mSeriesRecordingListeners.add(listener); 100 } 101 102 @Override 103 public final void removeSeriesRecordingListener(SeriesRecordingListener listener) { 104 mSeriesRecordingListeners.remove(listener); 105 } 106 107 @Override 108 public final void addRecordedProgramListener(RecordedProgramListener listener) { 109 mRecordedProgramListeners.add(listener); 110 } 111 112 @Override 113 public final void removeRecordedProgramListener(RecordedProgramListener listener) { 114 mRecordedProgramListeners.remove(listener); 115 } 116 117 /** 118 * Calls {@link OnDvrScheduleLoadFinishedListener#onDvrScheduleLoadFinished} for each listener. 119 */ 120 protected final void notifyDvrScheduleLoadFinished() { 121 for (OnDvrScheduleLoadFinishedListener l : mOnDvrScheduleLoadFinishedListeners) { 122 if (DEBUG) Log.d(TAG, "notify DVR schedule load finished"); 123 l.onDvrScheduleLoadFinished(); 124 } 125 } 126 127 /** 128 * Calls {@link OnRecordedProgramLoadFinishedListener#onRecordedProgramLoadFinished()} for each 129 * listener. 130 */ 131 protected final void notifyRecordedProgramLoadFinished() { 132 for (OnRecordedProgramLoadFinishedListener l : mOnRecordedProgramLoadFinishedListeners) { 133 if (DEBUG) Log.d(TAG, "notify recorded programs load finished"); 134 l.onRecordedProgramLoadFinished(); 135 } 136 } 137 138 /** Calls {@link RecordedProgramListener#onRecordedProgramsAdded} for each listener. */ 139 protected final void notifyRecordedProgramsAdded(RecordedProgram... recordedPrograms) { 140 for (RecordedProgramListener l : mRecordedProgramListeners) { 141 if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(recordedPrograms)); 142 l.onRecordedProgramsAdded(recordedPrograms); 143 } 144 } 145 146 /** Calls {@link RecordedProgramListener#onRecordedProgramsChanged} for each listener. */ 147 protected final void notifyRecordedProgramsChanged(RecordedProgram... recordedPrograms) { 148 for (RecordedProgramListener l : mRecordedProgramListeners) { 149 if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(recordedPrograms)); 150 l.onRecordedProgramsChanged(recordedPrograms); 151 } 152 } 153 154 /** Calls {@link RecordedProgramListener#onRecordedProgramsRemoved} for each listener. */ 155 protected final void notifyRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { 156 for (RecordedProgramListener l : mRecordedProgramListeners) { 157 if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(recordedPrograms)); 158 l.onRecordedProgramsRemoved(recordedPrograms); 159 } 160 } 161 162 /** Calls {@link SeriesRecordingListener#onSeriesRecordingAdded} for each listener. */ 163 protected final void notifySeriesRecordingAdded(SeriesRecording... seriesRecordings) { 164 for (SeriesRecordingListener l : mSeriesRecordingListeners) { 165 if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(seriesRecordings)); 166 l.onSeriesRecordingAdded(seriesRecordings); 167 } 168 } 169 170 /** Calls {@link SeriesRecordingListener#onSeriesRecordingRemoved} for each listener. */ 171 protected final void notifySeriesRecordingRemoved(SeriesRecording... seriesRecordings) { 172 for (SeriesRecordingListener l : mSeriesRecordingListeners) { 173 if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(seriesRecordings)); 174 l.onSeriesRecordingRemoved(seriesRecordings); 175 } 176 } 177 178 /** Calls {@link SeriesRecordingListener#onSeriesRecordingChanged} for each listener. */ 179 protected final void notifySeriesRecordingChanged(SeriesRecording... seriesRecordings) { 180 for (SeriesRecordingListener l : mSeriesRecordingListeners) { 181 if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(seriesRecordings)); 182 l.onSeriesRecordingChanged(seriesRecordings); 183 } 184 } 185 186 /** Calls {@link ScheduledRecordingListener#onScheduledRecordingAdded} for each listener. */ 187 protected final void notifyScheduledRecordingAdded(ScheduledRecording... scheduledRecording) { 188 for (ScheduledRecordingListener l : mScheduledRecordingListeners) { 189 if (DEBUG) Log.d(TAG, "notify " + l + " added " + Arrays.asList(scheduledRecording)); 190 l.onScheduledRecordingAdded(scheduledRecording); 191 } 192 } 193 194 /** Calls {@link ScheduledRecordingListener#onScheduledRecordingRemoved} for each listener. */ 195 protected final void notifyScheduledRecordingRemoved(ScheduledRecording... scheduledRecording) { 196 for (ScheduledRecordingListener l : mScheduledRecordingListeners) { 197 if (DEBUG) Log.d(TAG, "notify " + l + " removed " + Arrays.asList(scheduledRecording)); 198 l.onScheduledRecordingRemoved(scheduledRecording); 199 } 200 } 201 202 /** 203 * Calls {@link ScheduledRecordingListener#onScheduledRecordingStatusChanged} for each listener. 204 */ 205 protected final void notifyScheduledRecordingStatusChanged( 206 ScheduledRecording... scheduledRecording) { 207 for (ScheduledRecordingListener l : mScheduledRecordingListeners) { 208 if (DEBUG) Log.d(TAG, "notify " + l + " changed " + Arrays.asList(scheduledRecording)); 209 l.onScheduledRecordingStatusChanged(scheduledRecording); 210 } 211 } 212 213 /** 214 * Returns a new list with only {@link ScheduledRecording} with a {@link 215 * ScheduledRecording#getEndTimeMs() endTime} after now. 216 */ 217 private List<ScheduledRecording> filterEndTimeIsPast(List<ScheduledRecording> originals) { 218 List<ScheduledRecording> results = new ArrayList<>(originals.size()); 219 for (ScheduledRecording r : originals) { 220 if (r.getEndTimeMs() > mClock.currentTimeMillis()) { 221 results.add(r); 222 } 223 } 224 return results; 225 } 226 227 @Override 228 public List<ScheduledRecording> getAvailableScheduledRecordings() { 229 return filterEndTimeIsPast( 230 getRecordingsWithState( 231 ScheduledRecording.STATE_RECORDING_IN_PROGRESS, 232 ScheduledRecording.STATE_RECORDING_NOT_STARTED)); 233 } 234 235 @Override 236 public List<ScheduledRecording> getStartedRecordings() { 237 return filterEndTimeIsPast( 238 getRecordingsWithState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS)); 239 } 240 241 @Override 242 public List<ScheduledRecording> getNonStartedScheduledRecordings() { 243 return filterEndTimeIsPast( 244 getRecordingsWithState(ScheduledRecording.STATE_RECORDING_NOT_STARTED)); 245 } 246 247 @Override 248 public void changeState(ScheduledRecording scheduledRecording, @RecordingState int newState) { 249 if (scheduledRecording.getState() != newState) { 250 updateScheduledRecording( 251 ScheduledRecording.buildFrom(scheduledRecording).setState(newState).build()); 252 } 253 } 254 255 @Override 256 public Collection<ScheduledRecording> getDeletedSchedules() { 257 return mDeletedScheduleMap.values(); 258 } 259 260 @NonNull 261 @Override 262 public Collection<Long> getDisallowedProgramIds() { 263 return mDeletedScheduleMap.keySet(); 264 } 265 266 /** 267 * Returns the map which contains the deleted schedules which are mapped from the program ID. 268 */ 269 protected Map<Long, ScheduledRecording> getDeletedScheduleMap() { 270 return mDeletedScheduleMap; 271 } 272 273 /** Returns the schedules whose state is contained by states. */ 274 protected abstract List<ScheduledRecording> getRecordingsWithState(int... states); 275 276 @Override 277 public List<RecordedProgram> getRecordedPrograms(long seriesRecordingId) { 278 SeriesRecording seriesRecording = getSeriesRecording(seriesRecordingId); 279 if (seriesRecording == null) { 280 return Collections.emptyList(); 281 } 282 List<RecordedProgram> result = new ArrayList<>(); 283 for (RecordedProgram r : getRecordedPrograms()) { 284 if (seriesRecording.getSeriesId().equals(r.getSeriesId())) { 285 result.add(r); 286 } 287 } 288 return result; 289 } 290 291 @Override 292 public void checkAndRemoveEmptySeriesRecording(long... seriesRecordingIds) { 293 List<SeriesRecording> toRemove = new ArrayList<>(); 294 for (long rId : seriesRecordingIds) { 295 SeriesRecording seriesRecording = getSeriesRecording(rId); 296 if (seriesRecording != null && isEmptySeriesRecording(seriesRecording)) { 297 toRemove.add(seriesRecording); 298 } 299 } 300 removeSeriesRecording(SeriesRecording.toArray(toRemove)); 301 } 302 303 /** 304 * Returns {@code true}, if the series recording is empty and can be removed. If a series 305 * recording is in NORMAL state or has recordings or schedules, it is not empty and cannot be 306 * removed. 307 */ 308 protected final boolean isEmptySeriesRecording(@NonNull SeriesRecording seriesRecording) { 309 if (!seriesRecording.isStopped()) { 310 return false; 311 } 312 long seriesRecordingId = seriesRecording.getId(); 313 for (ScheduledRecording r : getAvailableScheduledRecordings()) { 314 if (r.getSeriesRecordingId() == seriesRecordingId) { 315 return false; 316 } 317 } 318 String seriesId = seriesRecording.getSeriesId(); 319 for (RecordedProgram r : getRecordedPrograms()) { 320 if (seriesId.equals(r.getSeriesId())) { 321 return false; 322 } 323 } 324 return true; 325 } 326 327 @Override 328 public void forgetStorage(String inputId) {} 329} 330