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.content.Context;
20 import android.support.annotation.MainThread;
21 import android.support.annotation.NonNull;
22 import android.support.annotation.Nullable;
23 import android.support.annotation.VisibleForTesting;
24 import android.util.Range;
25 
26 import com.android.tv.common.SoftPreconditions;
27 import com.android.tv.common.recording.RecordedProgram;
28 import com.android.tv.util.Clock;
29 
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.concurrent.atomic.AtomicLong;
36 
37 /**
38  * A DVR Data manager that stores values in memory suitable for testing.
39  */
40 @VisibleForTesting // TODO(DVR): move to testing dir.
41 @MainThread
42 public final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
43     private final static String TAG = "DvrDataManagerInMemory";
44     private final AtomicLong mNextId = new AtomicLong(1);
45     private final Map<Long, ScheduledRecording> mScheduledRecordings = new HashMap<>();
46     private final Map<Long, RecordedProgram> mRecordedPrograms = new HashMap<>();
47     private final List<SeasonRecording> mSeasonSchedule = new ArrayList<>();
48 
DvrDataManagerInMemoryImpl(Context context, Clock clock)49     public DvrDataManagerInMemoryImpl(Context context, Clock clock) {
50         super(context, clock);
51     }
52 
53     @Override
isInitialized()54     public boolean isInitialized() {
55         return true;
56     }
57 
getScheduledRecordingsPrograms()58     private List<ScheduledRecording> getScheduledRecordingsPrograms() {
59         return new ArrayList(mScheduledRecordings.values());
60     }
61 
62     @Override
getRecordedPrograms()63     public List<RecordedProgram> getRecordedPrograms() {
64         return new ArrayList<>(mRecordedPrograms.values());
65     }
66 
67     @Override
getAllScheduledRecordings()68     public List<ScheduledRecording> getAllScheduledRecordings() {
69         return new ArrayList<>(mScheduledRecordings.values());
70     }
71 
getSeasonRecordings()72     public List<SeasonRecording> getSeasonRecordings() {
73         return mSeasonSchedule;
74     }
75 
76     @Override
getNextScheduledStartTimeAfter(long startTime)77     public long getNextScheduledStartTimeAfter(long startTime) {
78 
79         List<ScheduledRecording> temp =  getNonStartedScheduledRecordings();
80         Collections.sort(temp, ScheduledRecording.START_TIME_COMPARATOR);
81         for (ScheduledRecording r : temp) {
82             if (r.getStartTimeMs() > startTime) {
83                 return r.getStartTimeMs();
84             }
85         }
86         return DvrDataManager.NEXT_START_TIME_NOT_FOUND;
87     }
88 
89     @Override
getRecordingsThatOverlapWith(Range<Long> period)90     public List<ScheduledRecording> getRecordingsThatOverlapWith(Range<Long> period) {
91         List<ScheduledRecording> temp = getScheduledRecordingsPrograms();
92         List<ScheduledRecording> result = new ArrayList<>();
93         for (ScheduledRecording r : temp) {
94             if (r.isOverLapping(period)) {
95                 result.add(r);
96             }
97         }
98         return result;
99     }
100 
101     /**
102      * Add a new scheduled recording.
103      */
104     @Override
addScheduledRecording(ScheduledRecording scheduledRecording)105     public void addScheduledRecording(ScheduledRecording scheduledRecording) {
106         addScheduledRecordingInternal(scheduledRecording);
107     }
108 
109 
addRecordedProgram(RecordedProgram recordedProgram)110     public void addRecordedProgram(RecordedProgram recordedProgram) {
111         addRecordedProgramInternal(recordedProgram);
112     }
113 
updateRecordedProgram(RecordedProgram r)114     public void updateRecordedProgram(RecordedProgram r) {
115         long id = r.getId();
116         if (mRecordedPrograms.containsKey(id)) {
117             mRecordedPrograms.put(id, r);
118             notifyRecordedProgramChanged(r);
119         } else {
120             throw new IllegalArgumentException("Recording not found:" + r);
121         }
122     }
123 
removeRecordedProgram(RecordedProgram scheduledRecording)124     public void removeRecordedProgram(RecordedProgram scheduledRecording) {
125         mRecordedPrograms.remove(scheduledRecording.getId());
126         notifyRecordedProgramRemoved(scheduledRecording);
127     }
128 
129 
addScheduledRecordingInternal(ScheduledRecording scheduledRecording)130     public ScheduledRecording addScheduledRecordingInternal(ScheduledRecording scheduledRecording) {
131         SoftPreconditions
132                 .checkState(scheduledRecording.getId() == ScheduledRecording.ID_NOT_SET, TAG,
133                         "expected id of " + ScheduledRecording.ID_NOT_SET + " but was "
134                                 + scheduledRecording);
135         scheduledRecording = ScheduledRecording.buildFrom(scheduledRecording)
136                 .setId(mNextId.incrementAndGet())
137                 .build();
138         mScheduledRecordings.put(scheduledRecording.getId(), scheduledRecording);
139         notifyScheduledRecordingAdded(scheduledRecording);
140         return scheduledRecording;
141     }
142 
addRecordedProgramInternal(RecordedProgram recordedProgram)143     public RecordedProgram addRecordedProgramInternal(RecordedProgram recordedProgram) {
144         SoftPreconditions.checkState(recordedProgram.getId() == RecordedProgram.ID_NOT_SET, TAG,
145                 "expected id of " + RecordedProgram.ID_NOT_SET + " but was " + recordedProgram);
146         recordedProgram = RecordedProgram.buildFrom(recordedProgram)
147                 .setId(mNextId.incrementAndGet())
148                 .build();
149         mRecordedPrograms.put(recordedProgram.getId(), recordedProgram);
150         notifyRecordedProgramAdded(recordedProgram);
151         return recordedProgram;
152     }
153 
154     @Override
addSeasonRecording(SeasonRecording seasonRecording)155     public void addSeasonRecording(SeasonRecording seasonRecording) {
156         mSeasonSchedule.add(seasonRecording);
157     }
158 
159     @Override
removeScheduledRecording(ScheduledRecording scheduledRecording)160     public void removeScheduledRecording(ScheduledRecording scheduledRecording) {
161         mScheduledRecordings.remove(scheduledRecording.getId());
162         notifyScheduledRecordingRemoved(scheduledRecording);
163     }
164 
165     @Override
removeSeasonSchedule(SeasonRecording seasonSchedule)166     public void removeSeasonSchedule(SeasonRecording seasonSchedule) {
167         mSeasonSchedule.remove(seasonSchedule);
168     }
169 
170     @Override
updateScheduledRecording(ScheduledRecording r)171     public void updateScheduledRecording(ScheduledRecording r) {
172         long id = r.getId();
173         if (mScheduledRecordings.containsKey(id)) {
174             mScheduledRecordings.put(id, r);
175             notifyScheduledRecordingStatusChanged(r);
176         } else {
177             throw new IllegalArgumentException("Recording not found:" + r);
178         }
179     }
180 
181     @Nullable
182     @Override
getScheduledRecording(long id)183     public ScheduledRecording getScheduledRecording(long id) {
184         return mScheduledRecordings.get(id);
185     }
186 
187     @Nullable
188     @Override
getScheduledRecordingForProgramId(long programId)189     public ScheduledRecording getScheduledRecordingForProgramId(long programId) {
190         for (ScheduledRecording r : mScheduledRecordings.values()) {
191             if (r.getProgramId() == programId) {
192                     return r;
193             }
194         }
195         return null;
196     }
197 
198     @Nullable
199     @Override
getRecordedProgram(long recordingId)200     public RecordedProgram getRecordedProgram(long recordingId) {
201         return mRecordedPrograms.get(recordingId);
202     }
203 
204     @Override
205     @NonNull
getRecordingsWithState(int state)206     protected List<ScheduledRecording> getRecordingsWithState(int state) {
207         ArrayList<ScheduledRecording> result = new ArrayList<>();
208         for (ScheduledRecording r : mScheduledRecordings.values()) {
209             if(r.getState() == state){
210                 result.add(r);
211             }
212         }
213         return result;
214     }
215 }
216