blob: 6d66eea1cdb50bd25fe44dd782291dd20633ec93 [file] [log] [blame]
/*
* 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;
}
}
}
}