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