1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/*
2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License.
6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at
7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software
11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and
14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License.
15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.guide;
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
19ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.MainThread;
202e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.support.annotation.Nullable;
210cc0713c1bf8027642987b750b80217569d2932aLive Channels Teamimport android.support.annotation.VisibleForTesting;
222e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.util.ArraySet;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log;
24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ChannelDataManager;
25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.GenreItems;
26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.Program;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.data.ProgramDataManager;
280cc0713c1bf8027642987b750b80217569d2932aLive Channels Teamimport com.android.tv.data.api.Channel;
292e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.dvr.DvrDataManager;
3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.dvr.DvrScheduleManager;
3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.dvr.DvrScheduleManager.OnConflictStateChangeListener;
32633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording;
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.TvInputManagerHelper;
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils;
35816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList;
36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.HashMap;
37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List;
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.Map;
397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport java.util.Set;
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.TimeUnit;
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
4295961816a768da387f0b5523cf4363ace2044089Nick Chalko/** Manages the channels and programs for the program guide. */
43ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko@MainThread
44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ProgramManager {
45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String TAG = "ProgramManager";
46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final boolean DEBUG = false;
47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * If the first entry's visible duration is shorter than this value, we clip the entry out.
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Note: If this value is larger than 1 min, it could cause mismatches between the entry's
51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * position and detailed view's time range.
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    static final long FIRST_ENTRY_MIN_DURATION = TimeUnit.MINUTES.toMillis(1);
54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final long INVALID_ID = -1;
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final TvInputManagerHelper mTvInputManagerHelper;
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ChannelDataManager mChannelDataManager;
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final ProgramDataManager mProgramDataManager;
6095961816a768da387f0b5523cf4363ace2044089Nick Chalko    private final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
6165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final DvrScheduleManager mDvrScheduleManager;
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mStartUtcMillis;
64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mEndUtcMillis;
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mFromUtcMillis;
66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private long mToUtcMillis;
67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private List<Channel> mChannels = new ArrayList<>();
69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Map<Long, List<TableEntry>> mChannelIdEntriesMap = new HashMap<>();
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final List<List<Channel>> mGenreChannelList = new ArrayList<>();
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final List<Integer> mFilteredGenreIds = new ArrayList<>();
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Position of selected genre to filter channel list.
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private int mSelectedGenreId = GenreItems.ID_ALL_CHANNELS;
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Channel list after applying genre filter.
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Should be matched with mSelectedGenreId always.
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private List<Channel> mFilteredChannels = mChannels;
7865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private boolean mChannelDataLoaded;
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
802e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final Set<Listener> mListeners = new ArraySet<>();
812e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final Set<TableEntriesUpdatedListener> mTableEntriesUpdatedListeners = new ArraySet<>();
822e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
832e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final Set<TableEntryChangedListener> mTableEntryChangedListeners = new ArraySet<>();
847d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
8565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final DvrDataManager.OnDvrScheduleLoadFinishedListener mDvrLoadedListener =
8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            new DvrDataManager.OnDvrScheduleLoadFinishedListener() {
8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                @Override
8865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                public void onDvrScheduleLoadFinished() {
8965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    if (mChannelDataLoaded) {
9065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        for (ScheduledRecording r : mDvrDataManager.getAllScheduledRecordings()) {
9165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            mScheduledRecordingListener.onScheduledRecordingAdded(r);
9265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        }
9365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mDvrDataManager.removeDvrScheduleLoadFinishedListener(this);
9565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
9665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            };
9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final ChannelDataManager.Listener mChannelDataManagerListener =
997d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            new ChannelDataManager.Listener() {
1007d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1017d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onLoadFinished() {
10265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mChannelDataLoaded = true;
103d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    updateChannels(false);
1047d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1057d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1077d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onChannelListUpdated() {
108d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    updateChannels(false);
1097d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onChannelBrowsableChanged() {
113d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    updateChannels(false);
1147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            };
1167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final ProgramDataManager.Listener mProgramDataManagerListener =
1187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            new ProgramDataManager.Listener() {
1197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                @Override
1207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                public void onProgramUpdated() {
121d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    updateTableEntries(true);
1227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            };
124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1252e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
1262e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            new DvrDataManager.ScheduledRecordingListener() {
12795961816a768da387f0b5523cf4363ace2044089Nick Chalko                @Override
12895961816a768da387f0b5523cf4363ace2044089Nick Chalko                public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
12995961816a768da387f0b5523cf4363ace2044089Nick Chalko                    for (ScheduledRecording schedule : scheduledRecordings) {
13095961816a768da387f0b5523cf4363ace2044089Nick Chalko                        TableEntry oldEntry = getTableEntry(schedule);
13195961816a768da387f0b5523cf4363ace2044089Nick Chalko                        if (oldEntry != null) {
13295961816a768da387f0b5523cf4363ace2044089Nick Chalko                            TableEntry newEntry =
13395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    new TableEntry(
13495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.channelId,
13595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.program,
13695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            schedule,
13795961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryStartUtcMillis,
13895961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryEndUtcMillis,
13995961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.isBlocked());
14095961816a768da387f0b5523cf4363ace2044089Nick Chalko                            updateEntry(oldEntry, newEntry);
14195961816a768da387f0b5523cf4363ace2044089Nick Chalko                        }
14295961816a768da387f0b5523cf4363ace2044089Nick Chalko                    }
14365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
1442e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
14595961816a768da387f0b5523cf4363ace2044089Nick Chalko                @Override
14695961816a768da387f0b5523cf4363ace2044089Nick Chalko                public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
14795961816a768da387f0b5523cf4363ace2044089Nick Chalko                    for (ScheduledRecording schedule : scheduledRecordings) {
14895961816a768da387f0b5523cf4363ace2044089Nick Chalko                        TableEntry oldEntry = getTableEntry(schedule);
14995961816a768da387f0b5523cf4363ace2044089Nick Chalko                        if (oldEntry != null) {
15095961816a768da387f0b5523cf4363ace2044089Nick Chalko                            TableEntry newEntry =
15195961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    new TableEntry(
15295961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.channelId,
15395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.program,
15495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            null,
15595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryStartUtcMillis,
15695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryEndUtcMillis,
15795961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.isBlocked());
15895961816a768da387f0b5523cf4363ace2044089Nick Chalko                            updateEntry(oldEntry, newEntry);
15995961816a768da387f0b5523cf4363ace2044089Nick Chalko                        }
16095961816a768da387f0b5523cf4363ace2044089Nick Chalko                    }
16165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
1622e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
16395961816a768da387f0b5523cf4363ace2044089Nick Chalko                @Override
16495961816a768da387f0b5523cf4363ace2044089Nick Chalko                public void onScheduledRecordingStatusChanged(
16595961816a768da387f0b5523cf4363ace2044089Nick Chalko                        ScheduledRecording... scheduledRecordings) {
16695961816a768da387f0b5523cf4363ace2044089Nick Chalko                    for (ScheduledRecording schedule : scheduledRecordings) {
16795961816a768da387f0b5523cf4363ace2044089Nick Chalko                        TableEntry oldEntry = getTableEntry(schedule);
16895961816a768da387f0b5523cf4363ace2044089Nick Chalko                        if (oldEntry != null) {
16995961816a768da387f0b5523cf4363ace2044089Nick Chalko                            TableEntry newEntry =
17095961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    new TableEntry(
17195961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.channelId,
17295961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.program,
17395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            schedule,
17495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryStartUtcMillis,
17595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.entryEndUtcMillis,
17695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            oldEntry.isBlocked());
17795961816a768da387f0b5523cf4363ace2044089Nick Chalko                            updateEntry(oldEntry, newEntry);
17895961816a768da387f0b5523cf4363ace2044089Nick Chalko                        }
17995961816a768da387f0b5523cf4363ace2044089Nick Chalko                    }
18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
18195961816a768da387f0b5523cf4363ace2044089Nick Chalko            };
1822e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final OnConflictStateChangeListener mOnConflictStateChangeListener =
18465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            new OnConflictStateChangeListener() {
18565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                @Override
18695961816a768da387f0b5523cf4363ace2044089Nick Chalko                public void onConflictStateChange(
18795961816a768da387f0b5523cf4363ace2044089Nick Chalko                        boolean conflict, ScheduledRecording... schedules) {
18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    for (ScheduledRecording schedule : schedules) {
18965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        TableEntry entry = getTableEntry(schedule);
19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        if (entry != null) {
19165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                            notifyTableEntryUpdated(entry);
19265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        }
19365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    }
19465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
19565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            };
19665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
19795961816a768da387f0b5523cf4363ace2044089Nick Chalko    public ProgramManager(
19895961816a768da387f0b5523cf4363ace2044089Nick Chalko            TvInputManagerHelper tvInputManagerHelper,
19995961816a768da387f0b5523cf4363ace2044089Nick Chalko            ChannelDataManager channelDataManager,
20095961816a768da387f0b5523cf4363ace2044089Nick Chalko            ProgramDataManager programDataManager,
20165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            @Nullable DvrDataManager dvrDataManager,
20265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            @Nullable DvrScheduleManager dvrScheduleManager) {
203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTvInputManagerHelper = tvInputManagerHelper;
204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannelDataManager = channelDataManager;
205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramDataManager = programDataManager;
2062e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mDvrDataManager = dvrDataManager;
20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mDvrScheduleManager = dvrScheduleManager;
208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
210633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void programGuideVisibilityChanged(boolean visible) {
211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mProgramDataManager.setPauseProgramUpdate(visible);
2127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (visible) {
2137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mChannelDataManager.addListener(mChannelDataManagerListener);
2147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mProgramDataManager.addListener(mProgramDataManagerListener);
2152e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            if (mDvrDataManager != null) {
21665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                if (!mDvrDataManager.isDvrScheduleLoadFinished()) {
21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    mDvrDataManager.addDvrScheduleLoadFinishedListener(mDvrLoadedListener);
21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                }
2192e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                mDvrDataManager.addScheduledRecordingListener(mScheduledRecordingListener);
2202e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
22165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (mDvrScheduleManager != null) {
22265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mDvrScheduleManager.addOnConflictStateChangeListener(
22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        mOnConflictStateChangeListener);
22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
2257d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        } else {
2267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mChannelDataManager.removeListener(mChannelDataManagerListener);
2277d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mProgramDataManager.removeListener(mProgramDataManagerListener);
2282e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            if (mDvrDataManager != null) {
22965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mDvrDataManager.removeDvrScheduleLoadFinishedListener(mDvrLoadedListener);
2302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                mDvrDataManager.removeScheduledRecordingListener(mScheduledRecordingListener);
2312e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
23265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (mDvrScheduleManager != null) {
23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                mDvrScheduleManager.removeOnConflictStateChangeListener(
23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                        mOnConflictStateChangeListener);
23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
2367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
237816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
238816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
23995961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Adds a {@link Listener}. */
240633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void addListener(Listener listener) {
241816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListeners.add(listener);
242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
24495961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Registers a listener to be invoked when table entries are updated. */
245633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void addTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTableEntriesUpdatedListeners.add(listener);
247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
24995961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Registers a listener to be invoked when a table entry is changed. */
250633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void addTableEntryChangedListener(TableEntryChangedListener listener) {
2512e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mTableEntryChangedListeners.add(listener);
2522e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
2532e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
25495961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Removes a {@link Listener}. */
255633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void removeListener(Listener listener) {
256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mListeners.remove(listener);
257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
25995961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Removes a previously installed table entries update listener. */
260633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void removeTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
261816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mTableEntriesUpdatedListeners.remove(listener);
262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
26495961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Removes a previously installed table entry changed listener. */
265633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void removeTableEntryChangedListener(TableEntryChangedListener listener) {
2662e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        mTableEntryChangedListeners.remove(listener);
2672e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
2682e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
2692e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    /**
27095961816a768da387f0b5523cf4363ace2044089Nick Chalko     * Resets channel list with given genre. Caller should call {@link #buildGenreFilters()} prior
27195961816a768da387f0b5523cf4363ace2044089Nick Chalko     * to call this API to make This notifies channel updates to listeners.
272816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
273633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void resetChannelListWithGenre(int genreId) {
274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (genreId == mSelectedGenreId) {
275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            return;
276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFilteredChannels = mGenreChannelList.get(genreId);
278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSelectedGenreId = genreId;
279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) {
28095961816a768da387f0b5523cf4363ace2044089Nick Chalko            Log.d(
28195961816a768da387f0b5523cf4363ace2044089Nick Chalko                    TAG,
28295961816a768da387f0b5523cf4363ace2044089Nick Chalko                    "resetChannelListWithGenre: "
28395961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + GenreItems.getCanonicalGenre(genreId)
28495961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + " has "
28595961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + mFilteredChannels.size()
28695961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + " channels out of "
28795961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + mChannels.size());
288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mGenreChannelList.get(mSelectedGenreId) == null) {
290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            throw new IllegalStateException("Genre filter isn't ready.");
291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        notifyChannelsUpdated();
293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
29595961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Update the initial time range to manage. It updates program entries and genre as well. */
296633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void updateInitialTimeRange(long startUtcMillis, long endUtcMillis) {
297633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mStartUtcMillis = startUtcMillis;
298633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (endUtcMillis > mEndUtcMillis) {
299633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            mEndUtcMillis = endUtcMillis;
300633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
301633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
302633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mProgramDataManager.setPrefetchTimeRange(mStartUtcMillis);
303633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        updateChannels(true);
304633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        setTimeRange(startUtcMillis, endUtcMillis);
305633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
306633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
30795961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Shifts the time range by the given time. Also makes ProgramGuide scroll the views. */
308633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    void shiftTime(long timeMillisToScroll) {
309633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        long fromUtcMillis = mFromUtcMillis + timeMillisToScroll;
310633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        long toUtcMillis = mToUtcMillis + timeMillisToScroll;
311633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (fromUtcMillis < mStartUtcMillis) {
312633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            fromUtcMillis = mStartUtcMillis;
313633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            toUtcMillis += mStartUtcMillis - fromUtcMillis;
314633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
315633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (toUtcMillis > mEndUtcMillis) {
316633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            fromUtcMillis -= toUtcMillis - mEndUtcMillis;
317633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            toUtcMillis = mEndUtcMillis;
318633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
319633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        setTimeRange(fromUtcMillis, toUtcMillis);
320633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
321633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
32295961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returned the scrolled(shifted) time in milliseconds. */
323633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    long getShiftedTime() {
324633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mFromUtcMillis - mStartUtcMillis;
325633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
326633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
32795961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the start time set by {@link #updateInitialTimeRange}. */
328633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    long getStartTime() {
329633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mStartUtcMillis;
330633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
331633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
33295961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the program index of the program with {@code entryId} or -1 if not found. */
333633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getProgramIdIndex(long channelId, long entryId) {
334633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
335633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (entries != null) {
336633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            for (int i = 0; i < entries.size(); i++) {
337633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                if (entries.get(i).getId() == entryId) {
338633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                    return i;
339633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                }
340633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            }
341633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
342633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return -1;
343633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
344633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
34595961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the program index of the program at {@code time} or -1 if not found. */
346633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getProgramIndexAtTime(long channelId, long time) {
347633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
348633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (int i = 0; i < entries.size(); ++i) {
349633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            TableEntry entry = entries.get(i);
35095961816a768da387f0b5523cf4363ace2044089Nick Chalko            if (entry.entryStartUtcMillis <= time && time < entry.entryEndUtcMillis) {
351633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                return i;
352633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            }
353633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
354633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return -1;
355633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
356633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
35795961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the start time of currently managed time range, in UTC millisecond. */
358633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    long getFromUtcMillis() {
359633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mFromUtcMillis;
360633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
361633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
36295961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the end time of currently managed time range, in UTC millisecond. */
363633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    long getToUtcMillis() {
364633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mToUtcMillis;
365633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
366633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
36795961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns the number of the currently managed channels. */
368633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getChannelCount() {
369633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mFilteredChannels.size();
370633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
371633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
372633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
373633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Returns a {@link Channel} at a given {@code channelIndex} of the currently managed channels.
374633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Returns {@code null} if such a channel is not found.
375633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
376633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    Channel getChannel(int channelIndex) {
377633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (channelIndex < 0 || channelIndex >= getChannelCount()) {
378633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return null;
379633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
380633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mFilteredChannels.get(channelIndex);
381633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
382633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
383633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
38495961816a768da387f0b5523cf4363ace2044089Nick Chalko     * Returns the index of provided {@link Channel} within the currently managed channels. Returns
38595961816a768da387f0b5523cf4363ace2044089Nick Chalko     * -1 if such a channel is not found.
386633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
387633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getChannelIndex(Channel channel) {
388633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mFilteredChannels.indexOf(channel);
389633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
390633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
391633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
39295961816a768da387f0b5523cf4363ace2044089Nick Chalko     * Returns the index of channel with {@code channelId} within the currently managed channels.
393633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Returns -1 if such a channel is not found.
394633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
395633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getChannelIndex(long channelId) {
396633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return getChannelIndex(mChannelDataManager.getChannel(channelId));
397633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
398633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
399633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
400633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Returns the number of "entries", which lies within the currently managed time range, for a
401633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * given {@code channelId}.
402633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
403633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getTableEntryCount(long channelId) {
404633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mChannelIdEntriesMap.get(channelId).size();
405633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
406633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
407633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
408633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Returns an entry as {@link Program} for a given {@code channelId} and {@code index} of
409633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * entries within the currently managed time range. Returned {@link Program} can be a dummy one
410633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * (e.g., whose channelId is INVALID_ID), when it corresponds to a gap between programs.
411633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
412633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    TableEntry getTableEntry(long channelId, int index) {
413633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        return mChannelIdEntriesMap.get(channelId).get(index);
414633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
415633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
41695961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Returns list genre ID's which has a channel. */
417633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    List<Integer> getFilteredGenreIds() {
418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mFilteredGenreIds;
419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
421633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    int getSelectedGenreId() {
422816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return mSelectedGenreId;
423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // Note that This can be happens only if program guide isn't shown
426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    // because an user has to select channels as browsable through UI.
427d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void updateChannels(boolean clearPreviousTableEntries) {
428816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) Log.d(TAG, "updateChannels");
429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mChannels = mChannelDataManager.getBrowsableChannelList();
430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mSelectedGenreId = GenreItems.ID_ALL_CHANNELS;
431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mFilteredChannels = mChannels;
432633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        updateTableEntriesWithoutNotification(clearPreviousTableEntries);
433633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        // Channel update notification should be called after updating table entries, so that
434633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        // the listener can get the entries.
435d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        notifyChannelsUpdated();
436633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        notifyTableEntriesUpdated();
437633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        buildGenreFilters();
438816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
440d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko    private void updateTableEntries(boolean clear) {
441633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        updateTableEntriesWithoutNotification(clear);
442633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        notifyTableEntriesUpdated();
443633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        buildGenreFilters();
444633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
445633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
44695961816a768da387f0b5523cf4363ace2044089Nick Chalko    /** Updates the table entries without notifying the change. */
447633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void updateTableEntriesWithoutNotification(boolean clear) {
448816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (clear) {
449816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelIdEntriesMap.clear();
450816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
45195961816a768da387f0b5523cf4363ace2044089Nick Chalko        boolean parentalControlsEnabled =
45295961816a768da387f0b5523cf4363ace2044089Nick Chalko                mTvInputManagerHelper.getParentalControlSettings().isParentalControlsEnabled();
453816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        for (Channel channel : mChannels) {
454816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            long channelId = channel.getId();
455816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // Inline the updating of the mChannelIdEntriesMap here so we can only call
456816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            // getParentalControlSettings once.
457816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            List<TableEntry> entries = createProgramEntries(channelId, parentalControlsEnabled);
458816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mChannelIdEntriesMap.put(channelId, entries);
459816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
460816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            int size = entries.size();
461816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (DEBUG) {
46295961816a768da387f0b5523cf4363ace2044089Nick Chalko                Log.d(
46395961816a768da387f0b5523cf4363ace2044089Nick Chalko                        TAG,
46495961816a768da387f0b5523cf4363ace2044089Nick Chalko                        "Programs are loaded for channel "
46595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                + channel.getId()
46695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                + ", loaded size = "
46795961816a768da387f0b5523cf4363ace2044089Nick Chalko                                + size);
468816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
469816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (size == 0) {
470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                continue;
471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TableEntry lastEntry = entries.get(size - 1);
473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (mEndUtcMillis < lastEntry.entryEndUtcMillis
474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    && lastEntry.entryEndUtcMillis != Long.MAX_VALUE) {
475816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                mEndUtcMillis = lastEntry.entryEndUtcMillis;
476816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mEndUtcMillis > mStartUtcMillis) {
479816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (Channel channel : mChannels) {
480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                long channelId = channel.getId();
481816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (entries.isEmpty()) {
483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    entries.add(new TableEntry(channelId, mStartUtcMillis, mEndUtcMillis));
484816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                } else {
485816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    TableEntry lastEntry = entries.get(entries.size() - 1);
486816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    if (mEndUtcMillis > lastEntry.entryEndUtcMillis) {
48795961816a768da387f0b5523cf4363ace2044089Nick Chalko                        entries.add(
48895961816a768da387f0b5523cf4363ace2044089Nick Chalko                                new TableEntry(
48995961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        channelId, lastEntry.entryEndUtcMillis, mEndUtcMillis));
490816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    } else if (lastEntry.entryEndUtcMillis == Long.MAX_VALUE) {
491816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                        entries.remove(entries.size() - 1);
49295961816a768da387f0b5523cf4363ace2044089Nick Chalko                        entries.add(
49395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                new TableEntry(
49495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        lastEntry.channelId,
49595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        lastEntry.program,
49695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        lastEntry.scheduledRecording,
49795961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        lastEntry.entryStartUtcMillis,
49895961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        mEndUtcMillis,
49995961816a768da387f0b5523cf4363ace2044089Nick Chalko                                        lastEntry.mIsBlocked));
500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    }
501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
504633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
505721bd0da688cd552737fbb753a00597f95103b95Adrian Roos
506633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    /**
50795961816a768da387f0b5523cf4363ace2044089Nick Chalko     * Build genre filters based on the current programs. This categories channels by its current
50895961816a768da387f0b5523cf4363ace2044089Nick Chalko     * program's canonical genres and subsequent @{link resetChannelListWithGenre(int)} calls will
50995961816a768da387f0b5523cf4363ace2044089Nick Chalko     * reset channel list with built channel list. This is expected to be called whenever program
51095961816a768da387f0b5523cf4363ace2044089Nick Chalko     * guide is shown.
511633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     */
512633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void buildGenreFilters() {
513633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        if (DEBUG) Log.d(TAG, "buildGenreFilters");
514816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
515633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mGenreChannelList.clear();
516633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (int i = 0; i < GenreItems.getGenreCount(); i++) {
517633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            mGenreChannelList.add(new ArrayList<>());
518816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
519633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (Channel channel : mChannels) {
520633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            Program currentProgram = mProgramDataManager.getCurrentProgram(channel.getId());
521633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            if (currentProgram != null && currentProgram.getCanonicalGenres() != null) {
522633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                for (String genre : currentProgram.getCanonicalGenres()) {
523633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                    mGenreChannelList.get(GenreItems.getId(genre)).add(channel);
524633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                }
525633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            }
526816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
527633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mGenreChannelList.set(GenreItems.ID_ALL_CHANNELS, mChannels);
528633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mFilteredGenreIds.clear();
529633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mFilteredGenreIds.add(0);
530633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (int i = 1; i < GenreItems.getGenreCount(); i++) {
531633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            if (mGenreChannelList.get(i).size() > 0) {
532633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko                mFilteredGenreIds.add(i);
533633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            }
5342e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
535633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mSelectedGenreId = GenreItems.ID_ALL_CHANNELS;
536633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        mFilteredChannels = mChannels;
537633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        notifyGenresUpdated();
5382e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
5392e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
5402e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    @Nullable
5412e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private TableEntry getTableEntry(ScheduledRecording scheduledRecording) {
5422e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        return getTableEntry(scheduledRecording.getChannelId(), scheduledRecording.getProgramId());
5432e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
5442e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
5452e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    @Nullable
5462e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    private TableEntry getTableEntry(long channelId, long entryId) {
5472e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
5482e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        if (entries != null) {
5492e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            for (TableEntry entry : entries) {
5502e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                if (entry.getId() == entryId) {
5512e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                    return entry;
5522e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko                }
5532e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko            }
5542e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        }
5552e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko        return null;
5562e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
5572e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
558633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void updateEntry(TableEntry old, TableEntry newEntry) {
559633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        List<TableEntry> entries = mChannelIdEntriesMap.get(old.channelId);
560633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        int index = entries.indexOf(old);
561633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        entries.set(index, newEntry);
562633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        notifyTableEntryUpdated(newEntry);
563816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
564816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
565816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void setTimeRange(long fromUtcMillis, long toUtcMillis) {
566816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (DEBUG) {
56795961816a768da387f0b5523cf4363ace2044089Nick Chalko            Log.d(
56895961816a768da387f0b5523cf4363ace2044089Nick Chalko                    TAG,
56995961816a768da387f0b5523cf4363ace2044089Nick Chalko                    "setTimeRange. {FromTime="
57095961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + Utils.toTimeString(fromUtcMillis)
57195961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + ", ToTime="
57295961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + Utils.toTimeString(toUtcMillis)
57395961816a768da387f0b5523cf4363ace2044089Nick Chalko                            + "}");
574816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
575816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (mFromUtcMillis != fromUtcMillis || mToUtcMillis != toUtcMillis) {
576816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mFromUtcMillis = fromUtcMillis;
577816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            mToUtcMillis = toUtcMillis;
578816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            notifyTimeRangeUpdated();
579816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
580816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
581816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
582816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private List<TableEntry> createProgramEntries(long channelId, boolean parentalControlsEnabled) {
583816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        List<TableEntry> entries = new ArrayList<>();
58495961816a768da387f0b5523cf4363ace2044089Nick Chalko        boolean channelLocked =
58595961816a768da387f0b5523cf4363ace2044089Nick Chalko                parentalControlsEnabled && mChannelDataManager.getChannel(channelId).isLocked();
586816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (channelLocked) {
587816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            entries.add(new TableEntry(channelId, mStartUtcMillis, Long.MAX_VALUE, true));
588816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        } else {
589816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            long lastProgramEndTime = mStartUtcMillis;
590816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            List<Program> programs = mProgramDataManager.getPrograms(channelId, mStartUtcMillis);
591816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            for (Program program : programs) {
592816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (program.getChannelId() == INVALID_ID) {
593816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // Dummy program.
594816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    continue;
595816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
59695961816a768da387f0b5523cf4363ace2044089Nick Chalko                long programStartTime = Math.max(program.getStartTimeUtcMillis(), mStartUtcMillis);
597816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                long programEndTime = program.getEndTimeUtcMillis();
598816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (programStartTime > lastProgramEndTime) {
599816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    // Gap since the last program.
60095961816a768da387f0b5523cf4363ace2044089Nick Chalko                    entries.add(new TableEntry(channelId, lastProgramEndTime, programStartTime));
601816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    lastProgramEndTime = programStartTime;
602816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
603816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                if (programEndTime > lastProgramEndTime) {
60495961816a768da387f0b5523cf4363ace2044089Nick Chalko                    ScheduledRecording scheduledRecording =
60595961816a768da387f0b5523cf4363ace2044089Nick Chalko                            mDvrDataManager == null
60695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    ? null
60795961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    : mDvrDataManager.getScheduledRecordingForProgramId(
60895961816a768da387f0b5523cf4363ace2044089Nick Chalko                                            program.getId());
60995961816a768da387f0b5523cf4363ace2044089Nick Chalko                    entries.add(
61095961816a768da387f0b5523cf4363ace2044089Nick Chalko                            new TableEntry(
61195961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    channelId,
61295961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    program,
61395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    scheduledRecording,
61495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    lastProgramEndTime,
61595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    programEndTime,
61695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                    false));
617816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                    lastProgramEndTime = programEndTime;
618816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                }
619816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
620816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
621816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
622816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        if (entries.size() > 1) {
623816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            TableEntry secondEntry = entries.get(1);
624816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (secondEntry.entryStartUtcMillis < mStartUtcMillis + FIRST_ENTRY_MIN_DURATION) {
625816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // If the first entry's width doesn't have enough width, it is not good to show
626816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                // the first entry from UI perspective. So we clip it out.
627816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                entries.remove(0);
62895961816a768da387f0b5523cf4363ace2044089Nick Chalko                entries.set(
62995961816a768da387f0b5523cf4363ace2044089Nick Chalko                        0,
63095961816a768da387f0b5523cf4363ace2044089Nick Chalko                        new TableEntry(
63195961816a768da387f0b5523cf4363ace2044089Nick Chalko                                secondEntry.channelId,
63295961816a768da387f0b5523cf4363ace2044089Nick Chalko                                secondEntry.program,
63395961816a768da387f0b5523cf4363ace2044089Nick Chalko                                secondEntry.scheduledRecording,
63495961816a768da387f0b5523cf4363ace2044089Nick Chalko                                mStartUtcMillis,
63595961816a768da387f0b5523cf4363ace2044089Nick Chalko                                secondEntry.entryEndUtcMillis,
63695961816a768da387f0b5523cf4363ace2044089Nick Chalko                                secondEntry.mIsBlocked));
637816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
638816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
639816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return entries;
640816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
641816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
642633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void notifyGenresUpdated() {
643633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (Listener listener : mListeners) {
644633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            listener.onGenresUpdated();
645633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
646816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
647816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
648633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void notifyChannelsUpdated() {
649633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (Listener listener : mListeners) {
650633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            listener.onChannelsUpdated();
651633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
652816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
653816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
654633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void notifyTimeRangeUpdated() {
655633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (Listener listener : mListeners) {
656633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            listener.onTimeRangeUpdated();
657633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
6582e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
6592e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
660633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void notifyTableEntriesUpdated() {
661633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (TableEntriesUpdatedListener listener : mTableEntriesUpdatedListeners) {
662633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            listener.onTableEntriesUpdated();
663633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
664633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    }
665816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
666633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    private void notifyTableEntryUpdated(TableEntry entry) {
667633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        for (TableEntryChangedListener listener : mTableEntryChangedListeners) {
668633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            listener.onTableEntryChanged(entry);
669633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
670816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
671816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
672816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
673633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko     * Entry for program guide table. An "entry" can be either an actual program or a gap between
67495961816a768da387f0b5523cf4363ace2044089Nick Chalko     * programs. This is needed for {@link ProgramListAdapter} because {@link
67595961816a768da387f0b5523cf4363ace2044089Nick Chalko     * android.support.v17.leanback.widget.HorizontalGridView} ignores margins between items.
676816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
677633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    static class TableEntry {
678633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        /** Channel ID which this entry is included. */
679633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        final long channelId;
680633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
681633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        /** Program corresponding to the entry. {@code null} means that this entry is a gap. */
682633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        final Program program;
683633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
684633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        final ScheduledRecording scheduledRecording;
685633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
686633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        /** Start time of entry in UTC milliseconds. */
687633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        final long entryStartUtcMillis;
688633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
689633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        /** End time of entry in UTC milliseconds */
690633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        final long entryEndUtcMillis;
691633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
692633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        private final boolean mIsBlocked;
693633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
694633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        private TableEntry(long channelId, long startUtcMillis, long endUtcMillis) {
695633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this(channelId, null, startUtcMillis, endUtcMillis, false);
696816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
697633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
69895961816a768da387f0b5523cf4363ace2044089Nick Chalko        private TableEntry(
69995961816a768da387f0b5523cf4363ace2044089Nick Chalko                long channelId, long startUtcMillis, long endUtcMillis, boolean blocked) {
700633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this(channelId, null, null, startUtcMillis, endUtcMillis, blocked);
701633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
702633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
70395961816a768da387f0b5523cf4363ace2044089Nick Chalko        private TableEntry(
70495961816a768da387f0b5523cf4363ace2044089Nick Chalko                long channelId,
70595961816a768da387f0b5523cf4363ace2044089Nick Chalko                Program program,
70695961816a768da387f0b5523cf4363ace2044089Nick Chalko                long entryStartUtcMillis,
70795961816a768da387f0b5523cf4363ace2044089Nick Chalko                long entryEndUtcMillis,
70895961816a768da387f0b5523cf4363ace2044089Nick Chalko                boolean isBlocked) {
709633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this(channelId, program, null, entryStartUtcMillis, entryEndUtcMillis, isBlocked);
710633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
711633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
71295961816a768da387f0b5523cf4363ace2044089Nick Chalko        private TableEntry(
71395961816a768da387f0b5523cf4363ace2044089Nick Chalko                long channelId,
71495961816a768da387f0b5523cf4363ace2044089Nick Chalko                Program program,
71595961816a768da387f0b5523cf4363ace2044089Nick Chalko                ScheduledRecording scheduledRecording,
71695961816a768da387f0b5523cf4363ace2044089Nick Chalko                long entryStartUtcMillis,
71795961816a768da387f0b5523cf4363ace2044089Nick Chalko                long entryEndUtcMillis,
71895961816a768da387f0b5523cf4363ace2044089Nick Chalko                boolean isBlocked) {
719633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this.channelId = channelId;
720633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this.program = program;
721633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this.scheduledRecording = scheduledRecording;
722633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this.entryStartUtcMillis = entryStartUtcMillis;
723633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            this.entryEndUtcMillis = entryEndUtcMillis;
724633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            mIsBlocked = isBlocked;
725633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
726633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
72795961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}. */
728633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        long getId() {
729633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            // using a negative entryEndUtcMillis keeps it from conflicting with program Id
730633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return program != null ? program.getId() : -entryEndUtcMillis;
731633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
732633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
73395961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** Returns true if this is a gap. */
734633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        boolean isGap() {
7350cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            return !Program.isProgramValid(program);
736633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
737633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
73895961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** Returns true if this channel is blocked. */
739633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        boolean isBlocked() {
740633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return mIsBlocked;
741633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
742633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
74395961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** Returns true if this program is on the air. */
744633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        boolean isCurrentProgram() {
745633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            long current = System.currentTimeMillis();
746633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return entryStartUtcMillis <= current && entryEndUtcMillis > current;
747633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
748633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
74995961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** Returns if this program has the genre. */
750633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        boolean hasGenre(int genreId) {
751633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return !isGap() && program.hasGenre(genreId);
752633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
753633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
75495961816a768da387f0b5523cf4363ace2044089Nick Chalko        /** Returns the width of table entry, in pixels. */
755633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        int getWidth() {
756633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return GuideUtils.convertMillisToPixel(entryStartUtcMillis, entryEndUtcMillis);
757633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        }
758633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
759633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        @Override
760633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        public String toString() {
761633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko            return "TableEntry{"
76295961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + "hashCode="
76395961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + hashCode()
76495961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + ", channelId="
76595961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + channelId
76695961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + ", program="
76795961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + program
76895961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + ", startTime="
76995961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + Utils.toTimeString(entryStartUtcMillis)
77095961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + ", endTimeTime="
77195961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + Utils.toTimeString(entryEndUtcMillis)
77295961816a768da387f0b5523cf4363ace2044089Nick Chalko                    + "}";
773816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
774816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
775816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
7760cc0713c1bf8027642987b750b80217569d2932aLive Channels Team    @VisibleForTesting
7770cc0713c1bf8027642987b750b80217569d2932aLive Channels Team    public static TableEntry createTableEntryForTest(
7780cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            long channelId,
7790cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            Program program,
7800cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            ScheduledRecording scheduledRecording,
7810cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            long entryStartUtcMillis,
7820cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            long entryEndUtcMillis,
7830cc0713c1bf8027642987b750b80217569d2932aLive Channels Team            boolean isBlocked) {
7840cc0713c1bf8027642987b750b80217569d2932aLive Channels Team        return new TableEntry(
7850cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                channelId,
7860cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                program,
7870cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                scheduledRecording,
7880cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                entryStartUtcMillis,
7890cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                entryEndUtcMillis,
7900cc0713c1bf8027642987b750b80217569d2932aLive Channels Team                isBlocked);
7910cc0713c1bf8027642987b750b80217569d2932aLive Channels Team    }
7920cc0713c1bf8027642987b750b80217569d2932aLive Channels Team
793633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    interface Listener {
794633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        void onGenresUpdated();
79595961816a768da387f0b5523cf4363ace2044089Nick Chalko
796633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        void onChannelsUpdated();
79795961816a768da387f0b5523cf4363ace2044089Nick Chalko
798633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        void onTimeRangeUpdated();
799816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
800816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
801633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    interface TableEntriesUpdatedListener {
802633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        void onTableEntriesUpdated();
803816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
804816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
805633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    interface TableEntryChangedListener {
806633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        void onTableEntryChanged(TableEntry entry);
8072e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko    }
8082e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko
809633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko    static class ListenerAdapter implements Listener {
810633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        @Override
81195961816a768da387f0b5523cf4363ace2044089Nick Chalko        public void onGenresUpdated() {}
812633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
813633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        @Override
81495961816a768da387f0b5523cf4363ace2044089Nick Chalko        public void onChannelsUpdated() {}
815633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko
816633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko        @Override
81795961816a768da387f0b5523cf4363ace2044089Nick Chalko        public void onTimeRangeUpdated() {}
818816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
819816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
820