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
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context;
20ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread;
21ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.NonNull;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.Nullable;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.VisibleForTesting;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Range;
251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.SoftPreconditions;
2748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.recording.RecordedProgram;
2848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.util.Clock;
29ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Collections;
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashMap;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Map;
35ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport java.util.concurrent.atomic.AtomicLong;
361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * A DVR Data manager that stores values in memory suitable for testing.
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko@VisibleForTesting // TODO(DVR): move to testing dir.
41ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko@MainThread
421abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic final class DvrDataManagerInMemoryImpl extends BaseDvrDataManager {
43ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private final static String TAG = "DvrDataManagerInMemory";
44ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    private final AtomicLong mNextId = new AtomicLong(1);
4548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final Map<Long, ScheduledRecording> mScheduledRecordings = new HashMap<>();
4648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final Map<Long, RecordedProgram> mRecordedPrograms = new HashMap<>();
4748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final List<SeasonRecording> mSeasonSchedule = new ArrayList<>();
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public DvrDataManagerInMemoryImpl(Context context, Clock clock) {
5048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        super(context, clock);
511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public boolean isInitialized() {
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
5848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private List<ScheduledRecording> getScheduledRecordingsPrograms() {
5948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return new ArrayList(mScheduledRecordings.values());
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
6348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public List<RecordedProgram> getRecordedPrograms() {
6448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return new ArrayList<>(mRecordedPrograms.values());
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
6848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public List<ScheduledRecording> getAllScheduledRecordings() {
6948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return new ArrayList<>(mScheduledRecordings.values());
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public List<SeasonRecording> getSeasonRecordings() {
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return mSeasonSchedule;
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public long getNextScheduledStartTimeAfter(long startTime) {
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        List<ScheduledRecording> temp =  getNonStartedScheduledRecordings();
8048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        Collections.sort(temp, ScheduledRecording.START_TIME_COMPARATOR);
8148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (ScheduledRecording r : temp) {
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (r.getStartTimeMs() > startTime) {
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return r.getStartTimeMs();
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return DvrDataManager.NEXT_START_TIME_NOT_FOUND;
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
9048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public List<ScheduledRecording> getRecordingsThatOverlapWith(Range<Long> period) {
9148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        List<ScheduledRecording> temp = getScheduledRecordingsPrograms();
9248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        List<ScheduledRecording> result = new ArrayList<>();
9348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (ScheduledRecording r : temp) {
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (r.isOverLapping(period)) {
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result.add(r);
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return result;
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
10248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho     * Add a new scheduled recording.
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
10548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void addScheduledRecording(ScheduledRecording scheduledRecording) {
10648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        addScheduledRecordingInternal(scheduledRecording);
10748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
10848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
10948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
11048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void addRecordedProgram(RecordedProgram recordedProgram) {
11148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        addRecordedProgramInternal(recordedProgram);
11248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
11348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
11448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void updateRecordedProgram(RecordedProgram r) {
11548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        long id = r.getId();
11648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mRecordedPrograms.containsKey(id)) {
11748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mRecordedPrograms.put(id, r);
11848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            notifyRecordedProgramChanged(r);
11948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        } else {
12048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            throw new IllegalArgumentException("Recording not found:" + r);
12148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
12248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
12348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
12448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void removeRecordedProgram(RecordedProgram scheduledRecording) {
12548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecordedPrograms.remove(scheduledRecording.getId());
12648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        notifyRecordedProgramRemoved(scheduledRecording);
12748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
12848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
12948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
13048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public ScheduledRecording addScheduledRecordingInternal(ScheduledRecording scheduledRecording) {
13148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        SoftPreconditions
13248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .checkState(scheduledRecording.getId() == ScheduledRecording.ID_NOT_SET, TAG,
13348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        "expected id of " + ScheduledRecording.ID_NOT_SET + " but was "
13448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                                + scheduledRecording);
13548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        scheduledRecording = ScheduledRecording.buildFrom(scheduledRecording)
13648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .setId(mNextId.incrementAndGet())
13748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .build();
13848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mScheduledRecordings.put(scheduledRecording.getId(), scheduledRecording);
13948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        notifyScheduledRecordingAdded(scheduledRecording);
14048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return scheduledRecording;
141ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
142ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
14348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public RecordedProgram addRecordedProgramInternal(RecordedProgram recordedProgram) {
14448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        SoftPreconditions.checkState(recordedProgram.getId() == RecordedProgram.ID_NOT_SET, TAG,
14548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                "expected id of " + RecordedProgram.ID_NOT_SET + " but was " + recordedProgram);
14648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        recordedProgram = RecordedProgram.buildFrom(recordedProgram)
14748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .setId(mNextId.incrementAndGet())
14848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                .build();
14948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mRecordedPrograms.put(recordedProgram.getId(), recordedProgram);
15048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        notifyRecordedProgramAdded(recordedProgram);
15148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return recordedProgram;
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void addSeasonRecording(SeasonRecording seasonRecording) {
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSeasonSchedule.add(seasonRecording);
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
16048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void removeScheduledRecording(ScheduledRecording scheduledRecording) {
16148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        mScheduledRecordings.remove(scheduledRecording.getId());
16248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        notifyScheduledRecordingRemoved(scheduledRecording);
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void removeSeasonSchedule(SeasonRecording seasonSchedule) {
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mSeasonSchedule.remove(seasonSchedule);
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
17148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public void updateScheduledRecording(ScheduledRecording r) {
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        long id = r.getId();
17348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (mScheduledRecordings.containsKey(id)) {
17448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            mScheduledRecordings.put(id, r);
17548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            notifyScheduledRecordingStatusChanged(r);
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            throw new IllegalArgumentException("Recording not found:" + r);
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Nullable
182ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @Override
18348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public ScheduledRecording getScheduledRecording(long id) {
18448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mScheduledRecordings.get(id);
1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
186ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko
18748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Nullable
18848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
18948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public ScheduledRecording getScheduledRecordingForProgramId(long programId) {
19048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (ScheduledRecording r : mScheduledRecordings.values()) {
19148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (r.getProgramId() == programId) {
19248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                    return r;
19348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
19448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
19548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return null;
19648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
19748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
19848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Nullable
19948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
20048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    public RecordedProgram getRecordedProgram(long recordingId) {
20148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        return mRecordedPrograms.get(recordingId);
20248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    }
20348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho
20448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    @Override
205ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    @NonNull
20648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    protected List<ScheduledRecording> getRecordingsWithState(int state) {
20748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        ArrayList<ScheduledRecording> result = new ArrayList<>();
20848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        for (ScheduledRecording r : mScheduledRecordings.values()) {
209ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            if(r.getState() == state){
210ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                result.add(r);
211ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko            }
212ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        }
213ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return result;
214ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko    }
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
216