1/*
2 * Copyright (C) 2017 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.recommendation;
18
19import android.content.Context;
20import android.os.Build;
21import android.support.annotation.RequiresApi;
22import android.text.TextUtils;
23import android.util.Log;
24import com.android.tv.TvSingletons;
25import com.android.tv.data.PreviewDataManager;
26import com.android.tv.data.PreviewProgramContent;
27import com.android.tv.dvr.DvrDataManager;
28import com.android.tv.dvr.data.RecordedProgram;
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.HashSet;
32import java.util.Set;
33
34/** Class to update the preview data for {@link RecordedProgram} */
35@RequiresApi(Build.VERSION_CODES.O)
36@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
37public class RecordedProgramPreviewUpdater {
38    private static final String TAG = "RecordedProgramPreviewUpdater";
39    private static final boolean DEBUG = false;
40
41    private static final int RECOMMENDATION_COUNT = 6;
42
43    private static RecordedProgramPreviewUpdater sRecordedProgramPreviewUpdater;
44
45    /** Creates and returns the {@link RecordedProgramPreviewUpdater}. */
46    public static RecordedProgramPreviewUpdater getInstance(Context context) {
47        if (sRecordedProgramPreviewUpdater == null) {
48            sRecordedProgramPreviewUpdater =
49                    new RecordedProgramPreviewUpdater(context.getApplicationContext());
50        }
51        return sRecordedProgramPreviewUpdater;
52    }
53
54    private final Context mContext;
55    private final PreviewDataManager mPreviewDataManager;
56    private final DvrDataManager mDvrDataManager;
57
58    private RecordedProgramPreviewUpdater(Context context) {
59        mContext = context.getApplicationContext();
60        TvSingletons tvSingletons = TvSingletons.getSingletons(mContext);
61        mPreviewDataManager = tvSingletons.getPreviewDataManager();
62        mDvrDataManager = tvSingletons.getDvrDataManager();
63        mDvrDataManager.addRecordedProgramListener(
64                new DvrDataManager.RecordedProgramListener() {
65                    @Override
66                    public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {
67                        if (DEBUG) Log.d(TAG, "Add new preview recorded programs");
68                        updatePreviewDataForRecordedPrograms();
69                    }
70
71                    @Override
72                    public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
73                        if (DEBUG) Log.d(TAG, "Update preview recorded programs");
74                        updatePreviewDataForRecordedPrograms();
75                    }
76
77                    @Override
78                    public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) {
79                        if (DEBUG) Log.d(TAG, "Delete preview recorded programs");
80                        updatePreviewDataForRecordedPrograms();
81                    }
82                });
83    }
84
85    /** Updates the preview data for recorded programs. */
86    public void updatePreviewDataForRecordedPrograms() {
87        if (!mPreviewDataManager.isLoadFinished()) {
88            mPreviewDataManager.addListener(
89                    new PreviewDataManager.PreviewDataListener() {
90                        @Override
91                        public void onPreviewDataLoadFinished() {
92                            mPreviewDataManager.removeListener(this);
93                            updatePreviewDataForRecordedPrograms();
94                        }
95
96                        @Override
97                        public void onPreviewDataUpdateFinished() {}
98                    });
99            return;
100        }
101        if (!mDvrDataManager.isRecordedProgramLoadFinished()) {
102            mDvrDataManager.addRecordedProgramLoadFinishedListener(
103                    new DvrDataManager.OnRecordedProgramLoadFinishedListener() {
104                        @Override
105                        public void onRecordedProgramLoadFinished() {
106                            mDvrDataManager.removeRecordedProgramLoadFinishedListener(this);
107                            updatePreviewDataForRecordedPrograms();
108                        }
109                    });
110            return;
111        }
112        updatePreviewDataForRecordedProgramsInternal();
113    }
114
115    private void updatePreviewDataForRecordedProgramsInternal() {
116        Set<RecordedProgram> recordedPrograms = generateRecommendationRecordedPrograms();
117        Long recordedPreviewChannelId =
118                mPreviewDataManager.getPreviewChannelId(
119                        PreviewDataManager.TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL);
120        if (recordedPreviewChannelId == PreviewDataManager.INVALID_PREVIEW_CHANNEL_ID
121                && !recordedPrograms.isEmpty()) {
122            createPreviewChannelForRecordedPrograms();
123        } else {
124            mPreviewDataManager.updatePreviewProgramsForChannel(
125                    recordedPreviewChannelId,
126                    generatePreviewProgramContentsFromRecordedPrograms(
127                            recordedPreviewChannelId, recordedPrograms),
128                    null);
129        }
130    }
131
132    private void createPreviewChannelForRecordedPrograms() {
133        mPreviewDataManager.createPreviewChannel(
134                PreviewDataManager.TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL,
135                new PreviewDataManager.OnPreviewChannelCreationResultListener() {
136                    @Override
137                    public void onPreviewChannelCreationResult(long createdPreviewChannelId) {
138                        if (createdPreviewChannelId
139                                != PreviewDataManager.INVALID_PREVIEW_CHANNEL_ID) {
140                            updatePreviewDataForRecordedProgramsInternal();
141                        }
142                    }
143                });
144    }
145
146    private Set<RecordedProgram> generateRecommendationRecordedPrograms() {
147        Set<RecordedProgram> programs = new HashSet<>();
148        ArrayList<RecordedProgram> sortedRecordedPrograms =
149                new ArrayList<>(mDvrDataManager.getRecordedPrograms());
150        Collections.sort(
151                sortedRecordedPrograms, RecordedProgram.START_TIME_THEN_ID_COMPARATOR.reversed());
152        for (RecordedProgram recordedProgram : sortedRecordedPrograms) {
153            if (!TextUtils.isEmpty(recordedProgram.getPosterArtUri())) {
154                programs.add(recordedProgram);
155                if (programs.size() >= RECOMMENDATION_COUNT) {
156                    break;
157                }
158            }
159        }
160        return programs;
161    }
162
163    private Set<PreviewProgramContent> generatePreviewProgramContentsFromRecordedPrograms(
164            long previewChannelId, Set<RecordedProgram> recordedPrograms) {
165        Set<PreviewProgramContent> result = new HashSet<>();
166        for (RecordedProgram recordedProgram : recordedPrograms) {
167            result.add(
168                    PreviewProgramContent.createFromRecordedProgram(
169                            mContext, previewChannelId, recordedProgram));
170        }
171        return result;
172    }
173}
174