| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.tv.dvr.ui.browse; |
| |
| import android.annotation.TargetApi; |
| import android.content.Context; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.support.v17.leanback.app.BrowseFragment; |
| import android.support.v17.leanback.widget.ArrayObjectAdapter; |
| import android.support.v17.leanback.widget.ClassPresenterSelector; |
| import android.support.v17.leanback.widget.HeaderItem; |
| import android.support.v17.leanback.widget.ListRow; |
| import android.support.v17.leanback.widget.Presenter; |
| import android.support.v17.leanback.widget.TitleViewAdapter; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; |
| import com.android.tv.R; |
| import com.android.tv.TvFeatures; |
| import com.android.tv.TvSingletons; |
| import com.android.tv.data.GenreItems; |
| import com.android.tv.dvr.DvrDataManager; |
| import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener; |
| import com.android.tv.dvr.DvrDataManager.OnRecordedProgramLoadFinishedListener; |
| import com.android.tv.dvr.DvrDataManager.RecordedProgramListener; |
| import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; |
| import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; |
| import com.android.tv.dvr.DvrScheduleManager; |
| import com.android.tv.dvr.data.RecordedProgram; |
| import com.android.tv.dvr.data.ScheduledRecording; |
| import com.android.tv.dvr.data.SeriesRecording; |
| import com.android.tv.dvr.ui.SortedArrayAdapter; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** {@link BrowseFragment} for DVR functions. */ |
| @TargetApi(Build.VERSION_CODES.N) |
| @SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated |
| public class DvrBrowseFragment extends BrowseFragment |
| implements RecordedProgramListener, |
| ScheduledRecordingListener, |
| SeriesRecordingListener, |
| OnDvrScheduleLoadFinishedListener, |
| OnRecordedProgramLoadFinishedListener { |
| private static final String TAG = "DvrBrowseFragment"; |
| private static final boolean DEBUG = false; |
| |
| private static final int MAX_RECENT_ITEM_COUNT = 10; |
| private static final int MAX_SCHEDULED_ITEM_COUNT = 4; |
| |
| private boolean mShouldShowScheduleRow; |
| private boolean mEntranceTransitionEnded; |
| |
| private RecordedProgramAdapter mRecentAdapter; |
| private ScheduleAdapter mScheduleAdapter; |
| private SeriesAdapter mSeriesAdapter; |
| private RecordedProgramAdapter[] mGenreAdapters = |
| new RecordedProgramAdapter[GenreItems.getGenreCount() + 1]; |
| private ListRow mRecentRow; |
| private ListRow mScheduledRow; |
| private ListRow mSeriesRow; |
| private ListRow[] mGenreRows = new ListRow[GenreItems.getGenreCount() + 1]; |
| private List<String> mGenreLabels; |
| private DvrDataManager mDvrDataManager; |
| private DvrScheduleManager mDvrScheudleManager; |
| private ArrayObjectAdapter mRowsAdapter; |
| private ClassPresenterSelector mPresenterSelector; |
| private final HashMap<String, RecordedProgram> mSeriesId2LatestProgram = new HashMap<>(); |
| private final Handler mHandler = new Handler(); |
| private final OnGlobalFocusChangeListener mOnGlobalFocusChangeListener = |
| new OnGlobalFocusChangeListener() { |
| @Override |
| public void onGlobalFocusChanged(View oldFocus, View newFocus) { |
| if (oldFocus instanceof RecordingCardView) { |
| ((RecordingCardView) oldFocus).expandTitle(false, true); |
| } |
| if (newFocus instanceof RecordingCardView) { |
| // If the header transition is ongoing, expand cards immediately without |
| // animation to make a smooth transition. |
| ((RecordingCardView) newFocus).expandTitle(true, !isInHeadersTransition()); |
| } |
| } |
| }; |
| |
| private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR = |
| new Comparator<Object>() { |
| @Override |
| public int compare(Object lhs, Object rhs) { |
| if (lhs instanceof SeriesRecording) { |
| lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId()); |
| } |
| if (rhs instanceof SeriesRecording) { |
| rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId()); |
| } |
| if (lhs instanceof RecordedProgram) { |
| if (rhs instanceof RecordedProgram) { |
| return RecordedProgram.START_TIME_THEN_ID_COMPARATOR |
| .reversed() |
| .compare((RecordedProgram) lhs, (RecordedProgram) rhs); |
| } else { |
| return -1; |
| } |
| } else if (rhs instanceof RecordedProgram) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| }; |
| |
| private static final Comparator<Object> SCHEDULE_COMPARATOR = |
| new Comparator<Object>() { |
| @Override |
| public int compare(Object lhs, Object rhs) { |
| if (lhs instanceof ScheduledRecording) { |
| if (rhs instanceof ScheduledRecording) { |
| return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR |
| .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs); |
| } else { |
| return -1; |
| } |
| } else if (rhs instanceof ScheduledRecording) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| }; |
| |
| private final DvrScheduleManager.OnConflictStateChangeListener mOnConflictStateChangeListener = |
| new DvrScheduleManager.OnConflictStateChangeListener() { |
| @Override |
| public void onConflictStateChange( |
| boolean conflict, ScheduledRecording... schedules) { |
| if (mScheduleAdapter != null) { |
| for (ScheduledRecording schedule : schedules) { |
| onScheduledRecordingConflictStatusChanged(schedule); |
| } |
| } |
| } |
| }; |
| |
| private final Runnable mUpdateRowsRunnable = |
| new Runnable() { |
| @Override |
| public void run() { |
| updateRows(); |
| } |
| }; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| if (DEBUG) Log.d(TAG, "onCreate"); |
| super.onCreate(savedInstanceState); |
| Context context = getContext(); |
| TvSingletons singletons = TvSingletons.getSingletons(context); |
| mDvrDataManager = singletons.getDvrDataManager(); |
| mDvrScheudleManager = singletons.getDvrScheduleManager(); |
| mPresenterSelector = |
| new ClassPresenterSelector() |
| .addClassPresenter( |
| ScheduledRecording.class, new ScheduledRecordingPresenter(context)) |
| .addClassPresenter( |
| RecordedProgram.class, new RecordedProgramPresenter(context)) |
| .addClassPresenter( |
| SeriesRecording.class, new SeriesRecordingPresenter(context)) |
| .addClassPresenter( |
| FullScheduleCardHolder.class, |
| new FullSchedulesCardPresenter(context)); |
| |
| if (TvFeatures.DVR_FAILED_LIST.isEnabled(context)) { |
| mPresenterSelector.addClassPresenter( |
| DvrHistoryCardHolder.class, |
| new DvrHistoryCardPresenter(context)); |
| } |
| mGenreLabels = new ArrayList<>(Arrays.asList(GenreItems.getLabels(context))); |
| mGenreLabels.add(getString(R.string.dvr_main_others)); |
| prepareUiElements(); |
| if (!startBrowseIfDvrInitialized()) { |
| if (!mDvrDataManager.isDvrScheduleLoadFinished()) { |
| mDvrDataManager.addDvrScheduleLoadFinishedListener(this); |
| } |
| if (!mDvrDataManager.isRecordedProgramLoadFinished()) { |
| mDvrDataManager.addRecordedProgramLoadFinishedListener(this); |
| } |
| } |
| } |
| |
| @Override |
| public void onViewCreated(View view, Bundle savedInstanceState) { |
| super.onViewCreated(view, savedInstanceState); |
| view.getViewTreeObserver().addOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener); |
| } |
| |
| @Override |
| public void onDestroyView() { |
| getView() |
| .getViewTreeObserver() |
| .removeOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener); |
| super.onDestroyView(); |
| } |
| |
| @Override |
| public void onDestroy() { |
| if (DEBUG) Log.d(TAG, "onDestroy"); |
| mHandler.removeCallbacks(mUpdateRowsRunnable); |
| mDvrScheudleManager.removeOnConflictStateChangeListener(mOnConflictStateChangeListener); |
| mDvrDataManager.removeRecordedProgramListener(this); |
| mDvrDataManager.removeScheduledRecordingListener(this); |
| mDvrDataManager.removeSeriesRecordingListener(this); |
| mDvrDataManager.removeDvrScheduleLoadFinishedListener(this); |
| mDvrDataManager.removeRecordedProgramLoadFinishedListener(this); |
| mRowsAdapter.clear(); |
| mSeriesId2LatestProgram.clear(); |
| for (Presenter presenter : mPresenterSelector.getPresenters()) { |
| if (presenter instanceof DvrItemPresenter) { |
| ((DvrItemPresenter) presenter).unbindAllViewHolders(); |
| } |
| } |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onDvrScheduleLoadFinished() { |
| startBrowseIfDvrInitialized(); |
| mDvrDataManager.removeDvrScheduleLoadFinishedListener(this); |
| } |
| |
| @Override |
| public void onRecordedProgramLoadFinished() { |
| startBrowseIfDvrInitialized(); |
| mDvrDataManager.removeRecordedProgramLoadFinishedListener(this); |
| } |
| |
| @Override |
| public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { |
| for (RecordedProgram recordedProgram : recordedPrograms) { |
| handleRecordedProgramAdded(recordedProgram, true); |
| } |
| postUpdateRows(); |
| } |
| |
| @Override |
| public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { |
| for (RecordedProgram recordedProgram : recordedPrograms) { |
| handleRecordedProgramChanged(recordedProgram); |
| } |
| postUpdateRows(); |
| } |
| |
| @Override |
| public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { |
| for (RecordedProgram recordedProgram : recordedPrograms) { |
| handleRecordedProgramRemoved(recordedProgram); |
| } |
| postUpdateRows(); |
| } |
| |
| // No need to call updateRows() during ScheduledRecordings' change because |
| // the row for ScheduledRecordings is always displayed. |
| @Override |
| public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { |
| for (ScheduledRecording scheduleRecording : scheduledRecordings) { |
| if (needToShowScheduledRecording(scheduleRecording)) { |
| mScheduleAdapter.add(scheduleRecording); |
| } |
| } |
| } |
| |
| @Override |
| public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { |
| for (ScheduledRecording scheduleRecording : scheduledRecordings) { |
| mScheduleAdapter.remove(scheduleRecording); |
| } |
| } |
| |
| @Override |
| public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) { |
| for (ScheduledRecording scheduleRecording : scheduledRecordings) { |
| if (needToShowScheduledRecording(scheduleRecording)) { |
| mScheduleAdapter.change(scheduleRecording); |
| } else { |
| mScheduleAdapter.removeWithId(scheduleRecording); |
| } |
| } |
| } |
| |
| private void onScheduledRecordingConflictStatusChanged(ScheduledRecording... schedules) { |
| for (ScheduledRecording schedule : schedules) { |
| if (needToShowScheduledRecording(schedule)) { |
| if (mScheduleAdapter.contains(schedule)) { |
| mScheduleAdapter.change(schedule); |
| } |
| } else { |
| mScheduleAdapter.removeWithId(schedule); |
| } |
| } |
| } |
| |
| @Override |
| public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { |
| handleSeriesRecordingsAdded(Arrays.asList(seriesRecordings)); |
| postUpdateRows(); |
| } |
| |
| @Override |
| public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { |
| handleSeriesRecordingsRemoved(Arrays.asList(seriesRecordings)); |
| postUpdateRows(); |
| } |
| |
| @Override |
| public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { |
| handleSeriesRecordingsChanged(Arrays.asList(seriesRecordings)); |
| postUpdateRows(); |
| } |
| |
| // Workaround of b/29108300 |
| @Override |
| public void showTitle(int flags) { |
| flags &= ~TitleViewAdapter.SEARCH_VIEW_VISIBLE; |
| super.showTitle(flags); |
| } |
| |
| @Override |
| protected void onEntranceTransitionEnd() { |
| super.onEntranceTransitionEnd(); |
| if (mShouldShowScheduleRow) { |
| showScheduledRowInternal(); |
| } |
| mEntranceTransitionEnded = true; |
| } |
| |
| void showScheduledRow() { |
| if (!mEntranceTransitionEnded) { |
| setHeadersState(HEADERS_HIDDEN); |
| mShouldShowScheduleRow = true; |
| } else { |
| showScheduledRowInternal(); |
| } |
| } |
| |
| private void showScheduledRowInternal() { |
| setSelectedPosition(mRowsAdapter.indexOf(mScheduledRow), true, null); |
| if (getHeadersState() == HEADERS_ENABLED) { |
| startHeadersTransition(false); |
| } |
| mShouldShowScheduleRow = false; |
| } |
| |
| private void prepareUiElements() { |
| setBadgeDrawable(getActivity().getDrawable(R.drawable.ic_dvr_badge)); |
| setHeadersState(HEADERS_ENABLED); |
| setHeadersTransitionOnBackEnabled(false); |
| setBrandColor(getResources().getColor(R.color.program_guide_side_panel_background, null)); |
| mRowsAdapter = new ArrayObjectAdapter(new DvrListRowPresenter(getContext())); |
| setAdapter(mRowsAdapter); |
| prepareEntranceTransition(); |
| } |
| |
| private boolean startBrowseIfDvrInitialized() { |
| if (mDvrDataManager.isInitialized()) { |
| // Setup rows |
| mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT); |
| mScheduleAdapter = new ScheduleAdapter(MAX_SCHEDULED_ITEM_COUNT); |
| mSeriesAdapter = new SeriesAdapter(); |
| for (int i = 0; i < mGenreAdapters.length; i++) { |
| mGenreAdapters[i] = new RecordedProgramAdapter(); |
| } |
| // Schedule Recordings. |
| List<ScheduledRecording> schedules = mDvrDataManager.getAllScheduledRecordings(); |
| onScheduledRecordingAdded(ScheduledRecording.toArray(schedules)); |
| mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER); |
| // Recorded Programs. |
| for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { |
| handleRecordedProgramAdded(recordedProgram, false); |
| } |
| if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())) { |
| mRecentAdapter.addExtraItem(DvrHistoryCardHolder.DVR_HISTORY_CARD_HOLDER); |
| } |
| // Series Recordings. Series recordings should be added after recorded programs, because |
| // we build series recordings' latest program information while adding recorded |
| // programs. |
| List<SeriesRecording> recordings = mDvrDataManager.getSeriesRecordings(); |
| handleSeriesRecordingsAdded(recordings); |
| mRecentRow = |
| new ListRow( |
| new HeaderItem(getString(R.string.dvr_main_recent)), mRecentAdapter); |
| mScheduledRow = |
| new ListRow( |
| new HeaderItem(getString(R.string.dvr_main_scheduled)), |
| mScheduleAdapter); |
| mSeriesRow = |
| new ListRow( |
| new HeaderItem(getString(R.string.dvr_main_series)), mSeriesAdapter); |
| mRowsAdapter.add(mScheduledRow); |
| updateRows(); |
| // Initialize listeners |
| mDvrDataManager.addRecordedProgramListener(this); |
| mDvrDataManager.addScheduledRecordingListener(this); |
| mDvrDataManager.addSeriesRecordingListener(this); |
| mDvrScheudleManager.addOnConflictStateChangeListener(mOnConflictStateChangeListener); |
| startEntranceTransition(); |
| return true; |
| } |
| return false; |
| } |
| |
| private void handleRecordedProgramAdded( |
| RecordedProgram recordedProgram, boolean updateSeriesRecording) { |
| mRecentAdapter.add(recordedProgram); |
| String seriesId = recordedProgram.getSeriesId(); |
| SeriesRecording seriesRecording = null; |
| if (seriesId != null) { |
| seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); |
| RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId); |
| if (latestProgram == null |
| || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare( |
| latestProgram, recordedProgram) |
| < 0) { |
| mSeriesId2LatestProgram.put(seriesId, recordedProgram); |
| if (updateSeriesRecording && seriesRecording != null) { |
| onSeriesRecordingChanged(seriesRecording); |
| } |
| } |
| } |
| if (seriesRecording == null) { |
| for (RecordedProgramAdapter adapter : |
| getGenreAdapters(recordedProgram.getCanonicalGenres())) { |
| adapter.add(recordedProgram); |
| } |
| } |
| } |
| |
| private void handleRecordedProgramRemoved(RecordedProgram recordedProgram) { |
| mRecentAdapter.remove(recordedProgram); |
| String seriesId = recordedProgram.getSeriesId(); |
| if (seriesId != null) { |
| SeriesRecording seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); |
| RecordedProgram latestProgram = |
| mSeriesId2LatestProgram.get(recordedProgram.getSeriesId()); |
| if (latestProgram != null && latestProgram.getId() == recordedProgram.getId()) { |
| if (seriesRecording != null) { |
| updateLatestRecordedProgram(seriesRecording); |
| onSeriesRecordingChanged(seriesRecording); |
| } |
| } |
| } |
| for (RecordedProgramAdapter adapter : |
| getGenreAdapters(recordedProgram.getCanonicalGenres())) { |
| adapter.remove(recordedProgram); |
| } |
| } |
| |
| private void handleRecordedProgramChanged(RecordedProgram recordedProgram) { |
| mRecentAdapter.change(recordedProgram); |
| String seriesId = recordedProgram.getSeriesId(); |
| SeriesRecording seriesRecording = null; |
| if (seriesId != null) { |
| seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); |
| RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId); |
| if (latestProgram == null |
| || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare( |
| latestProgram, recordedProgram) |
| <= 0) { |
| mSeriesId2LatestProgram.put(seriesId, recordedProgram); |
| if (seriesRecording != null) { |
| onSeriesRecordingChanged(seriesRecording); |
| } |
| } else if (latestProgram.getId() == recordedProgram.getId()) { |
| if (seriesRecording != null) { |
| updateLatestRecordedProgram(seriesRecording); |
| onSeriesRecordingChanged(seriesRecording); |
| } |
| } |
| } |
| if (seriesRecording == null) { |
| updateGenreAdapters( |
| getGenreAdapters(recordedProgram.getCanonicalGenres()), recordedProgram); |
| } else { |
| updateGenreAdapters(new ArrayList<>(), recordedProgram); |
| } |
| } |
| |
| private void handleSeriesRecordingsAdded(List<SeriesRecording> seriesRecordings) { |
| for (SeriesRecording seriesRecording : seriesRecordings) { |
| mSeriesAdapter.add(seriesRecording); |
| if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { |
| for (RecordedProgramAdapter adapter : |
| getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { |
| adapter.add(seriesRecording); |
| } |
| } |
| } |
| } |
| |
| private void handleSeriesRecordingsRemoved(List<SeriesRecording> seriesRecordings) { |
| for (SeriesRecording seriesRecording : seriesRecordings) { |
| mSeriesAdapter.remove(seriesRecording); |
| for (RecordedProgramAdapter adapter : |
| getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { |
| adapter.remove(seriesRecording); |
| } |
| } |
| } |
| |
| private void handleSeriesRecordingsChanged(List<SeriesRecording> seriesRecordings) { |
| for (SeriesRecording seriesRecording : seriesRecordings) { |
| mSeriesAdapter.change(seriesRecording); |
| if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { |
| updateGenreAdapters( |
| getGenreAdapters(seriesRecording.getCanonicalGenreIds()), seriesRecording); |
| } else { |
| // Remove series recording from all genre rows if it has no recorded program |
| updateGenreAdapters(new ArrayList<>(), seriesRecording); |
| } |
| } |
| } |
| |
| private List<RecordedProgramAdapter> getGenreAdapters(String[] genres) { |
| List<RecordedProgramAdapter> result = new ArrayList<>(); |
| if (genres == null || genres.length == 0) { |
| result.add(mGenreAdapters[mGenreAdapters.length - 1]); |
| } else { |
| for (String genre : genres) { |
| int genreId = GenreItems.getId(genre); |
| if (genreId >= mGenreAdapters.length) { |
| Log.d(TAG, "Wrong Genre ID: " + genreId); |
| } else { |
| result.add(mGenreAdapters[genreId]); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private List<RecordedProgramAdapter> getGenreAdapters(int[] genreIds) { |
| List<RecordedProgramAdapter> result = new ArrayList<>(); |
| if (genreIds == null || genreIds.length == 0) { |
| result.add(mGenreAdapters[mGenreAdapters.length - 1]); |
| } else { |
| for (int genreId : genreIds) { |
| if (genreId >= mGenreAdapters.length) { |
| Log.d(TAG, "Wrong Genre ID: " + genreId); |
| } else { |
| result.add(mGenreAdapters[genreId]); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private void updateGenreAdapters(List<RecordedProgramAdapter> adapters, Object r) { |
| for (RecordedProgramAdapter adapter : mGenreAdapters) { |
| if (adapters.contains(adapter)) { |
| adapter.change(r); |
| } else { |
| adapter.remove(r); |
| } |
| } |
| } |
| |
| private void postUpdateRows() { |
| mHandler.removeCallbacks(mUpdateRowsRunnable); |
| mHandler.post(mUpdateRowsRunnable); |
| } |
| |
| private void updateRows() { |
| int visibleRowsCount = 1; // Schedule's Row will never be empty |
| int recentRowMinSize = TvFeatures.DVR_FAILED_LIST.isEnabled(getContext()) ? 1 : 0; |
| if (mRecentAdapter.size() <= recentRowMinSize) { |
| mRowsAdapter.remove(mRecentRow); |
| } else { |
| if (mRowsAdapter.indexOf(mRecentRow) < 0) { |
| mRowsAdapter.add(0, mRecentRow); |
| } |
| visibleRowsCount++; |
| } |
| if (mSeriesAdapter.isEmpty()) { |
| mRowsAdapter.remove(mSeriesRow); |
| } else { |
| if (mRowsAdapter.indexOf(mSeriesRow) < 0) { |
| mRowsAdapter.add(visibleRowsCount, mSeriesRow); |
| } |
| visibleRowsCount++; |
| } |
| for (int i = 0; i < mGenreAdapters.length; i++) { |
| RecordedProgramAdapter adapter = mGenreAdapters[i]; |
| if (adapter != null) { |
| if (adapter.isEmpty()) { |
| mRowsAdapter.remove(mGenreRows[i]); |
| } else { |
| if (mGenreRows[i] == null || mRowsAdapter.indexOf(mGenreRows[i]) < 0) { |
| mGenreRows[i] = new ListRow(new HeaderItem(mGenreLabels.get(i)), adapter); |
| mRowsAdapter.add(visibleRowsCount, mGenreRows[i]); |
| } |
| visibleRowsCount++; |
| } |
| } |
| } |
| } |
| |
| private boolean needToShowScheduledRecording(ScheduledRecording recording) { |
| int state = recording.getState(); |
| return state == ScheduledRecording.STATE_RECORDING_IN_PROGRESS |
| || state == ScheduledRecording.STATE_RECORDING_NOT_STARTED; |
| } |
| |
| private void updateLatestRecordedProgram(SeriesRecording seriesRecording) { |
| RecordedProgram latestProgram = null; |
| for (RecordedProgram program : |
| mDvrDataManager.getRecordedPrograms(seriesRecording.getId())) { |
| if (latestProgram == null |
| || RecordedProgram.START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program) |
| < 0) { |
| latestProgram = program; |
| } |
| } |
| mSeriesId2LatestProgram.put(seriesRecording.getSeriesId(), latestProgram); |
| } |
| |
| private class ScheduleAdapter extends SortedArrayAdapter<Object> { |
| ScheduleAdapter(int maxItemCount) { |
| super(mPresenterSelector, SCHEDULE_COMPARATOR, maxItemCount); |
| } |
| |
| @Override |
| public long getId(Object item) { |
| if (item instanceof ScheduledRecording) { |
| return ((ScheduledRecording) item).getId(); |
| } else { |
| return -1; |
| } |
| } |
| } |
| |
| private class SeriesAdapter extends SortedArrayAdapter<SeriesRecording> { |
| SeriesAdapter() { |
| super( |
| mPresenterSelector, |
| new Comparator<SeriesRecording>() { |
| @Override |
| public int compare(SeriesRecording lhs, SeriesRecording rhs) { |
| if (lhs.isStopped() && !rhs.isStopped()) { |
| return 1; |
| } else if (!lhs.isStopped() && rhs.isStopped()) { |
| return -1; |
| } |
| return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs); |
| } |
| }); |
| } |
| |
| @Override |
| public long getId(SeriesRecording item) { |
| return item.getId(); |
| } |
| } |
| |
| private class RecordedProgramAdapter extends SortedArrayAdapter<Object> { |
| RecordedProgramAdapter() { |
| this(Integer.MAX_VALUE); |
| } |
| |
| RecordedProgramAdapter(int maxItemCount) { |
| super(mPresenterSelector, RECORDED_PROGRAM_COMPARATOR, maxItemCount); |
| } |
| |
| @Override |
| public long getId(Object item) { |
| // We takes the inverse number for the ID of recorded programs to make the ID stable. |
| if (item instanceof SeriesRecording) { |
| return ((SeriesRecording) item).getId(); |
| } else if (item instanceof RecordedProgram) { |
| return -((RecordedProgram) item).getId() - 1; |
| } else { |
| return -1; |
| } |
| } |
| } |
| } |