DvrWatchedPositionManager.java revision 633eb826b8c97731dfc5ef12c7bf78a63734275d
1/*
2 * Copyright (C) 2016 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.content.Context;
20import android.content.SharedPreferences;
21import android.media.tv.TvInputManager;
22import android.support.annotation.IntDef;
23
24import com.android.tv.common.SharedPreferencesUtils;
25import com.android.tv.dvr.data.RecordedProgram;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Set;
33import java.util.concurrent.CopyOnWriteArraySet;
34
35/**
36 * A class to manage DVR watched state.
37 * It will remember and provides previous watched position of DVR playback.
38 */
39public class DvrWatchedPositionManager {
40    private SharedPreferences mWatchedPositions;
41    private final Map<Long, Set> mListeners = new HashMap<>();
42
43    /**
44     * The minimum percentage of recorded program being watched that will be considered as being
45     * completely watched.
46     */
47    public static final float DVR_WATCHED_THRESHOLD_RATE = 0.98f;
48
49    @Retention(RetentionPolicy.SOURCE)
50    @IntDef({DVR_WATCHED_STATUS_NEW, DVR_WATCHED_STATUS_WATCHING, DVR_WATCHED_STATUS_WATCHED})
51    public @interface DvrWatchedStatus {}
52    /**
53     * The status indicates the recorded program has not been watched at all.
54     */
55    public static final int DVR_WATCHED_STATUS_NEW = 0;
56    /**
57     * The status indicates the recorded program is being watched.
58     */
59    public static final int DVR_WATCHED_STATUS_WATCHING = 1;
60    /**
61     * The status indicates the recorded program was completely watched.
62     */
63    public static final int DVR_WATCHED_STATUS_WATCHED = 2;
64
65    public DvrWatchedPositionManager(Context context) {
66        mWatchedPositions = context.getSharedPreferences(
67                SharedPreferencesUtils.SHARED_PREF_DVR_WATCHED_POSITION, Context.MODE_PRIVATE);
68    }
69
70    /**
71     * Sets the watched position of the give program.
72     */
73    public void setWatchedPosition(long recordedProgramId, long positionMs) {
74        mWatchedPositions.edit().putLong(Long.toString(recordedProgramId), positionMs).apply();
75        notifyWatchedPositionChanged(recordedProgramId, positionMs);
76    }
77
78    /**
79     * Gets the watched position of the give program.
80     */
81    public long getWatchedPosition(long recordedProgramId) {
82        return mWatchedPositions.getLong(Long.toString(recordedProgramId),
83                TvInputManager.TIME_SHIFT_INVALID_TIME);
84    }
85
86    @DvrWatchedStatus public int getWatchedStatus(RecordedProgram recordedProgram) {
87        long watchedPosition = getWatchedPosition(recordedProgram.getId());
88        if (watchedPosition == TvInputManager.TIME_SHIFT_INVALID_TIME) {
89            return DVR_WATCHED_STATUS_NEW;
90        } else if (watchedPosition > recordedProgram
91                .getDurationMillis() * DVR_WATCHED_THRESHOLD_RATE) {
92            return DVR_WATCHED_STATUS_WATCHED;
93        } else {
94            return DVR_WATCHED_STATUS_WATCHING;
95        }
96    }
97
98    /**
99     * Adds {@link WatchedPositionChangedListener}.
100     */
101    public void addListener(WatchedPositionChangedListener listener, long recordedProgramId) {
102        if (recordedProgramId == RecordedProgram.ID_NOT_SET) {
103            return;
104        }
105        Set<WatchedPositionChangedListener> listenerSet = mListeners.get(recordedProgramId);
106        if (listenerSet == null) {
107            listenerSet = new CopyOnWriteArraySet<>();
108            mListeners.put(recordedProgramId, listenerSet);
109        }
110        listenerSet.add(listener);
111    }
112
113    /**
114     * Removes {@link WatchedPositionChangedListener}.
115     */
116    public void removeListener(WatchedPositionChangedListener listener) {
117        for (long recordedProgramId : new ArrayList<>(mListeners.keySet())) {
118            removeListener(listener, recordedProgramId);
119        }
120    }
121
122    /**
123     * Removes {@link WatchedPositionChangedListener}.
124     */
125    public void removeListener(WatchedPositionChangedListener listener, long recordedProgramId) {
126        Set<WatchedPositionChangedListener> listenerSet = mListeners.get(recordedProgramId);
127        if (listenerSet == null) {
128            return;
129        }
130        listenerSet.remove(listener);
131        if (listenerSet.isEmpty()) {
132            mListeners.remove(recordedProgramId);
133        }
134    }
135
136    private void notifyWatchedPositionChanged(long recordedProgramId, long positionMs) {
137        Set<WatchedPositionChangedListener> listenerSet = mListeners.get(recordedProgramId);
138        if (listenerSet == null) {
139            return;
140        }
141        for (WatchedPositionChangedListener listener : listenerSet) {
142            listener.onWatchedPositionChanged(recordedProgramId, positionMs);
143        }
144    }
145
146    public interface WatchedPositionChangedListener {
147        /**
148         * Called when the watched position of some program is changed.
149         */
150        void onWatchedPositionChanged(long recordedProgramId, long positionMs);
151    }
152}
153