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