1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.tv.dvr.ui; 18 19 import android.content.Context; 20 import android.media.tv.TvInputManager.TvInputCallback; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.support.v17.leanback.app.BrowseFragment; 24 import android.support.v17.leanback.widget.ArrayObjectAdapter; 25 import android.support.v17.leanback.widget.ClassPresenterSelector; 26 import android.support.v17.leanback.widget.HeaderItem; 27 import android.support.v17.leanback.widget.ListRow; 28 import android.support.v17.leanback.widget.ListRowPresenter; 29 import android.support.v17.leanback.widget.Presenter; 30 import android.support.v17.leanback.widget.TitleViewAdapter; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import com.android.tv.ApplicationSingletons; 35 import com.android.tv.R; 36 import com.android.tv.TvApplication; 37 import com.android.tv.data.GenreItems; 38 import com.android.tv.dvr.DvrDataManager; 39 import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener; 40 import com.android.tv.dvr.DvrDataManager.OnRecordedProgramLoadFinishedListener; 41 import com.android.tv.dvr.DvrDataManager.RecordedProgramListener; 42 import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; 43 import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; 44 import com.android.tv.dvr.DvrScheduleManager; 45 import com.android.tv.dvr.RecordedProgram; 46 import com.android.tv.dvr.ScheduledRecording; 47 import com.android.tv.dvr.SeriesRecording; 48 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Comparator; 52 import java.util.HashMap; 53 import java.util.List; 54 55 /** 56 * {@link BrowseFragment} for DVR functions. 57 */ 58 public class DvrBrowseFragment extends BrowseFragment implements 59 RecordedProgramListener, ScheduledRecordingListener, SeriesRecordingListener, 60 OnDvrScheduleLoadFinishedListener, OnRecordedProgramLoadFinishedListener { 61 private static final String TAG = "DvrBrowseFragment"; 62 private static final boolean DEBUG = false; 63 64 private static final int MAX_RECENT_ITEM_COUNT = 10; 65 private static final int MAX_SCHEDULED_ITEM_COUNT = 4; 66 67 private RecordedProgramAdapter mRecentAdapter; 68 private ScheduleAdapter mScheduleAdapter; 69 private SeriesAdapter mSeriesAdapter; 70 private RecordedProgramAdapter[] mGenreAdapters = 71 new RecordedProgramAdapter[GenreItems.getGenreCount() + 1]; 72 private ListRow mRecentRow; 73 private ListRow mSeriesRow; 74 private ListRow[] mGenreRows = new ListRow[GenreItems.getGenreCount() + 1]; 75 private List<String> mGenreLabels; 76 private DvrDataManager mDvrDataManager; 77 private DvrScheduleManager mDvrScheudleManager; 78 private ArrayObjectAdapter mRowsAdapter; 79 private ClassPresenterSelector mPresenterSelector; 80 private final HashMap<String, RecordedProgram> mSeriesId2LatestProgram = new HashMap<>(); 81 private final Handler mHandler = new Handler(); 82 83 private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR = new Comparator<Object>() { 84 @Override 85 public int compare(Object lhs, Object rhs) { 86 if (lhs instanceof SeriesRecording) { 87 lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId()); 88 } 89 if (rhs instanceof SeriesRecording) { 90 rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId()); 91 } 92 if (lhs instanceof RecordedProgram) { 93 if (rhs instanceof RecordedProgram) { 94 return RecordedProgram.START_TIME_THEN_ID_COMPARATOR.reversed() 95 .compare((RecordedProgram) lhs, (RecordedProgram) rhs); 96 } else { 97 return -1; 98 } 99 } else if (rhs instanceof RecordedProgram) { 100 return 1; 101 } else { 102 return 0; 103 } 104 } 105 }; 106 107 private final Comparator<Object> SCHEDULE_COMPARATOR = new Comparator<Object>() { 108 @Override 109 public int compare(Object lhs, Object rhs) { 110 if (lhs instanceof ScheduledRecording) { 111 if (rhs instanceof ScheduledRecording) { 112 return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR 113 .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs); 114 } else { 115 return -1; 116 } 117 } else if (rhs instanceof ScheduledRecording) { 118 return 1; 119 } else { 120 return 0; 121 } 122 } 123 }; 124 125 private final DvrScheduleManager.OnConflictStateChangeListener mOnConflictStateChangeListener = 126 new DvrScheduleManager.OnConflictStateChangeListener() { 127 @Override 128 public void onConflictStateChange(boolean conflict, ScheduledRecording... schedules) { 129 if (mScheduleAdapter != null) { 130 for (ScheduledRecording schedule : schedules) { 131 onScheduledRecordingStatusChanged(schedule); 132 } 133 } 134 } 135 }; 136 137 private final Runnable mUpdateRowsRunnable = new Runnable() { 138 @Override 139 public void run() { 140 updateRows(); 141 } 142 }; 143 144 @Override onCreate(Bundle savedInstanceState)145 public void onCreate(Bundle savedInstanceState) { 146 if (DEBUG) Log.d(TAG, "onCreate"); 147 super.onCreate(savedInstanceState); 148 Context context = getContext(); 149 ApplicationSingletons singletons = TvApplication.getSingletons(context); 150 mDvrDataManager = singletons.getDvrDataManager(); 151 mDvrScheudleManager = singletons.getDvrScheduleManager(); 152 mPresenterSelector = new ClassPresenterSelector() 153 .addClassPresenter(ScheduledRecording.class, 154 new ScheduledRecordingPresenter(context)) 155 .addClassPresenter(RecordedProgram.class, new RecordedProgramPresenter(context)) 156 .addClassPresenter(SeriesRecording.class, new SeriesRecordingPresenter(context)) 157 .addClassPresenter(FullScheduleCardHolder.class, new FullSchedulesCardPresenter()); 158 mGenreLabels = new ArrayList<>(Arrays.asList(GenreItems.getLabels(context))); 159 mGenreLabels.add(getString(R.string.dvr_main_others)); 160 setupUiElements(); 161 setupAdapters(); 162 mDvrScheudleManager.addOnConflictStateChangeListener(mOnConflictStateChangeListener); 163 prepareEntranceTransition(); 164 if (mDvrDataManager.isInitialized()) { 165 startEntranceTransition(); 166 } else { 167 if (!mDvrDataManager.isDvrScheduleLoadFinished()) { 168 mDvrDataManager.addDvrScheduleLoadFinishedListener(this); 169 } 170 if (!mDvrDataManager.isRecordedProgramLoadFinished()) { 171 mDvrDataManager.addRecordedProgramLoadFinishedListener(this); 172 } 173 } 174 } 175 176 @Override onDestroy()177 public void onDestroy() { 178 if (DEBUG) Log.d(TAG, "onDestroy"); 179 mHandler.removeCallbacks(mUpdateRowsRunnable); 180 mDvrScheudleManager.removeOnConflictStateChangeListener(mOnConflictStateChangeListener); 181 mDvrDataManager.removeRecordedProgramListener(this); 182 mDvrDataManager.removeScheduledRecordingListener(this); 183 mDvrDataManager.removeSeriesRecordingListener(this); 184 mDvrDataManager.removeDvrScheduleLoadFinishedListener(this); 185 mDvrDataManager.removeRecordedProgramLoadFinishedListener(this); 186 mRowsAdapter.clear(); 187 mSeriesId2LatestProgram.clear(); 188 for (Presenter presenter : mPresenterSelector.getPresenters()) { 189 if (presenter instanceof DvrItemPresenter) { 190 ((DvrItemPresenter) presenter).unbindAllViewHolders(); 191 } 192 } 193 super.onDestroy(); 194 } 195 196 @Override onDvrScheduleLoadFinished()197 public void onDvrScheduleLoadFinished() { 198 List<ScheduledRecording> scheduledRecordings = mDvrDataManager.getAllScheduledRecordings(); 199 onScheduledRecordingAdded(ScheduledRecording.toArray(scheduledRecordings)); 200 List<SeriesRecording> seriesRecordings = mDvrDataManager.getSeriesRecordings(); 201 onSeriesRecordingAdded(SeriesRecording.toArray(seriesRecordings)); 202 if (mDvrDataManager.isInitialized()) { 203 startEntranceTransition(); 204 } 205 mDvrDataManager.removeDvrScheduleLoadFinishedListener(this); 206 } 207 208 @Override onRecordedProgramLoadFinished()209 public void onRecordedProgramLoadFinished() { 210 for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { 211 handleRecordedProgramAdded(recordedProgram, true); 212 } 213 updateRows(); 214 if (mDvrDataManager.isInitialized()) { 215 startEntranceTransition(); 216 } 217 mDvrDataManager.removeRecordedProgramLoadFinishedListener(this); 218 } 219 220 @Override onRecordedProgramsAdded(RecordedProgram... recordedPrograms)221 public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) { 222 for (RecordedProgram recordedProgram : recordedPrograms) { 223 handleRecordedProgramAdded(recordedProgram, true); 224 } 225 postUpdateRows(); 226 } 227 228 @Override onRecordedProgramsChanged(RecordedProgram... recordedPrograms)229 public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) { 230 for (RecordedProgram recordedProgram : recordedPrograms) { 231 handleRecordedProgramChanged(recordedProgram); 232 } 233 postUpdateRows(); 234 } 235 236 @Override onRecordedProgramsRemoved(RecordedProgram... recordedPrograms)237 public void onRecordedProgramsRemoved(RecordedProgram... recordedPrograms) { 238 for (RecordedProgram recordedProgram : recordedPrograms) { 239 handleRecordedProgramRemoved(recordedProgram); 240 } 241 postUpdateRows(); 242 } 243 244 // No need to call updateRows() during ScheduledRecordings' change because 245 // the row for ScheduledRecordings is always displayed. 246 @Override onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings)247 public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) { 248 for (ScheduledRecording scheduleRecording : scheduledRecordings) { 249 if (needToShowScheduledRecording(scheduleRecording)) { 250 mScheduleAdapter.add(scheduleRecording); 251 } 252 } 253 } 254 255 @Override onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings)256 public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) { 257 for (ScheduledRecording scheduleRecording : scheduledRecordings) { 258 mScheduleAdapter.remove(scheduleRecording); 259 } 260 } 261 262 @Override onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings)263 public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) { 264 for (ScheduledRecording scheduleRecording : scheduledRecordings) { 265 if (needToShowScheduledRecording(scheduleRecording)) { 266 mScheduleAdapter.change(scheduleRecording); 267 } else { 268 mScheduleAdapter.removeWithId(scheduleRecording); 269 } 270 } 271 } 272 273 @Override onSeriesRecordingAdded(SeriesRecording... seriesRecordings)274 public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { 275 handleSeriesRecordingsAdded(Arrays.asList(seriesRecordings)); 276 postUpdateRows(); 277 } 278 279 @Override onSeriesRecordingRemoved(SeriesRecording... seriesRecordings)280 public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { 281 handleSeriesRecordingsRemoved(Arrays.asList(seriesRecordings)); 282 postUpdateRows(); 283 } 284 285 @Override onSeriesRecordingChanged(SeriesRecording... seriesRecordings)286 public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { 287 handleSeriesRecordingsChanged(Arrays.asList(seriesRecordings)); 288 postUpdateRows(); 289 } 290 291 // Workaround of b/29108300 292 @Override showTitle(int flags)293 public void showTitle(int flags) { 294 flags &= ~TitleViewAdapter.SEARCH_VIEW_VISIBLE; 295 super.showTitle(flags); 296 } 297 setupUiElements()298 private void setupUiElements() { 299 setBadgeDrawable(getActivity().getDrawable(R.drawable.ic_dvr_badge)); 300 setHeadersState(HEADERS_ENABLED); 301 setHeadersTransitionOnBackEnabled(false); 302 setBrandColor(getResources().getColor(R.color.program_guide_side_panel_background, null)); 303 } 304 setupAdapters()305 private void setupAdapters() { 306 mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT); 307 mScheduleAdapter = new ScheduleAdapter(MAX_SCHEDULED_ITEM_COUNT); 308 mSeriesAdapter = new SeriesAdapter(); 309 for (int i = 0; i < mGenreAdapters.length; i++) { 310 mGenreAdapters[i] = new RecordedProgramAdapter(); 311 } 312 // Schedule Recordings. 313 List<ScheduledRecording> schedules = mDvrDataManager.getAllScheduledRecordings(); 314 onScheduledRecordingAdded(ScheduledRecording.toArray(schedules)); 315 mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER); 316 // Recorded Programs. 317 for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { 318 handleRecordedProgramAdded(recordedProgram, false); 319 } 320 // Series Recordings. Series recordings should be added after recorded programs, because 321 // we build series recordings' latest program information while adding recorded programs. 322 List<SeriesRecording> recordings = mDvrDataManager.getSeriesRecordings(); 323 handleSeriesRecordingsAdded(recordings); 324 mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); 325 mRecentRow = new ListRow(new HeaderItem( 326 getString(R.string.dvr_main_recent)), mRecentAdapter); 327 mRowsAdapter.add(new ListRow(new HeaderItem( 328 getString(R.string.dvr_main_scheduled)), mScheduleAdapter)); 329 mSeriesRow = new ListRow(new HeaderItem( 330 getString(R.string.dvr_main_series)), mSeriesAdapter); 331 updateRows(); 332 mDvrDataManager.addRecordedProgramListener(this); 333 mDvrDataManager.addScheduledRecordingListener(this); 334 mDvrDataManager.addSeriesRecordingListener(this); 335 setAdapter(mRowsAdapter); 336 } 337 handleRecordedProgramAdded(RecordedProgram recordedProgram, boolean updateSeriesRecording)338 private void handleRecordedProgramAdded(RecordedProgram recordedProgram, 339 boolean updateSeriesRecording) { 340 mRecentAdapter.add(recordedProgram); 341 String seriesId = recordedProgram.getSeriesId(); 342 SeriesRecording seriesRecording = null; 343 if (seriesId != null) { 344 seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); 345 RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId); 346 if (latestProgram == null || RecordedProgram.START_TIME_THEN_ID_COMPARATOR 347 .compare(latestProgram, recordedProgram) < 0) { 348 mSeriesId2LatestProgram.put(seriesId, recordedProgram); 349 if (updateSeriesRecording && seriesRecording != null) { 350 onSeriesRecordingChanged(seriesRecording); 351 } 352 } 353 } 354 if (seriesRecording == null) { 355 for (RecordedProgramAdapter adapter 356 : getGenreAdapters(recordedProgram.getCanonicalGenres())) { 357 adapter.add(recordedProgram); 358 } 359 } 360 } 361 handleRecordedProgramRemoved(RecordedProgram recordedProgram)362 private void handleRecordedProgramRemoved(RecordedProgram recordedProgram) { 363 mRecentAdapter.remove(recordedProgram); 364 String seriesId = recordedProgram.getSeriesId(); 365 if (seriesId != null) { 366 SeriesRecording seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); 367 RecordedProgram latestProgram = 368 mSeriesId2LatestProgram.get(recordedProgram.getSeriesId()); 369 if (latestProgram != null && latestProgram.getId() == recordedProgram.getId()) { 370 if (seriesRecording != null) { 371 updateLatestRecordedProgram(seriesRecording); 372 onSeriesRecordingChanged(seriesRecording); 373 } 374 } 375 } 376 for (RecordedProgramAdapter adapter 377 : getGenreAdapters(recordedProgram.getCanonicalGenres())) { 378 adapter.remove(recordedProgram); 379 } 380 } 381 handleRecordedProgramChanged(RecordedProgram recordedProgram)382 private void handleRecordedProgramChanged(RecordedProgram recordedProgram) { 383 mRecentAdapter.change(recordedProgram); 384 String seriesId = recordedProgram.getSeriesId(); 385 SeriesRecording seriesRecording = null; 386 if (seriesId != null) { 387 seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); 388 RecordedProgram latestProgram = mSeriesId2LatestProgram.get(seriesId); 389 if (latestProgram == null || RecordedProgram.START_TIME_THEN_ID_COMPARATOR 390 .compare(latestProgram, recordedProgram) <= 0) { 391 mSeriesId2LatestProgram.put(seriesId, recordedProgram); 392 if (seriesRecording != null) { 393 onSeriesRecordingChanged(seriesRecording); 394 } 395 } else if (latestProgram.getId() == recordedProgram.getId()) { 396 if (seriesRecording != null) { 397 updateLatestRecordedProgram(seriesRecording); 398 onSeriesRecordingChanged(seriesRecording); 399 } 400 } 401 } 402 if (seriesRecording == null) { 403 updateGenreAdapters(getGenreAdapters( 404 recordedProgram.getCanonicalGenres()), recordedProgram); 405 } else { 406 updateGenreAdapters(new ArrayList<>(), recordedProgram); 407 } 408 } 409 handleSeriesRecordingsAdded(List<SeriesRecording> seriesRecordings)410 private void handleSeriesRecordingsAdded(List<SeriesRecording> seriesRecordings) { 411 for (SeriesRecording seriesRecording : seriesRecordings) { 412 mSeriesAdapter.add(seriesRecording); 413 if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { 414 for (RecordedProgramAdapter adapter 415 : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { 416 adapter.add(seriesRecording); 417 } 418 } 419 } 420 } 421 handleSeriesRecordingsRemoved(List<SeriesRecording> seriesRecordings)422 private void handleSeriesRecordingsRemoved(List<SeriesRecording> seriesRecordings) { 423 for (SeriesRecording seriesRecording : seriesRecordings) { 424 mSeriesAdapter.remove(seriesRecording); 425 for (RecordedProgramAdapter adapter 426 : getGenreAdapters(seriesRecording.getCanonicalGenreIds())) { 427 adapter.remove(seriesRecording); 428 } 429 } 430 } 431 handleSeriesRecordingsChanged(List<SeriesRecording> seriesRecordings)432 private void handleSeriesRecordingsChanged(List<SeriesRecording> seriesRecordings) { 433 for (SeriesRecording seriesRecording : seriesRecordings) { 434 mSeriesAdapter.change(seriesRecording); 435 if (mSeriesId2LatestProgram.get(seriesRecording.getSeriesId()) != null) { 436 updateGenreAdapters(getGenreAdapters( 437 seriesRecording.getCanonicalGenreIds()), seriesRecording); 438 } else { 439 // Remove series recording from all genre rows if it has no recorded program 440 updateGenreAdapters(new ArrayList<>(), seriesRecording); 441 } 442 } 443 } 444 getGenreAdapters(String[] genres)445 private List<RecordedProgramAdapter> getGenreAdapters(String[] genres) { 446 List<RecordedProgramAdapter> result = new ArrayList<>(); 447 if (genres == null || genres.length == 0) { 448 result.add(mGenreAdapters[mGenreAdapters.length - 1]); 449 } else { 450 for (String genre : genres) { 451 int genreId = GenreItems.getId(genre); 452 if(genreId >= mGenreAdapters.length) { 453 Log.d(TAG, "Wrong Genre ID: " + genreId); 454 } else { 455 result.add(mGenreAdapters[genreId]); 456 } 457 } 458 } 459 return result; 460 } 461 getGenreAdapters(int[] genreIds)462 private List<RecordedProgramAdapter> getGenreAdapters(int[] genreIds) { 463 List<RecordedProgramAdapter> result = new ArrayList<>(); 464 if (genreIds == null || genreIds.length == 0) { 465 result.add(mGenreAdapters[mGenreAdapters.length - 1]); 466 } else { 467 for (int genreId : genreIds) { 468 if(genreId >= mGenreAdapters.length) { 469 Log.d(TAG, "Wrong Genre ID: " + genreId); 470 } else { 471 result.add(mGenreAdapters[genreId]); 472 } 473 } 474 } 475 return result; 476 } 477 updateGenreAdapters(List<RecordedProgramAdapter> adapters, Object r)478 private void updateGenreAdapters(List<RecordedProgramAdapter> adapters, Object r) { 479 for (RecordedProgramAdapter adapter : mGenreAdapters) { 480 if (adapters.contains(adapter)) { 481 adapter.change(r); 482 } else { 483 adapter.remove(r); 484 } 485 } 486 } 487 postUpdateRows()488 private void postUpdateRows() { 489 mHandler.removeCallbacks(mUpdateRowsRunnable); 490 mHandler.post(mUpdateRowsRunnable); 491 } 492 updateRows()493 private void updateRows() { 494 int visibleRowsCount = 1; // Schedule's Row will never be empty 495 if (mRecentAdapter.isEmpty()) { 496 mRowsAdapter.remove(mRecentRow); 497 } else { 498 if (mRowsAdapter.indexOf(mRecentRow) < 0) { 499 mRowsAdapter.add(0, mRecentRow); 500 } 501 visibleRowsCount++; 502 } 503 if (mSeriesAdapter.isEmpty()) { 504 mRowsAdapter.remove(mSeriesRow); 505 } else { 506 if (mRowsAdapter.indexOf(mSeriesRow) < 0) { 507 mRowsAdapter.add(visibleRowsCount, mSeriesRow); 508 } 509 visibleRowsCount++; 510 } 511 for (int i = 0; i < mGenreAdapters.length; i++) { 512 RecordedProgramAdapter adapter = mGenreAdapters[i]; 513 if (adapter != null) { 514 if (adapter.isEmpty()) { 515 mRowsAdapter.remove(mGenreRows[i]); 516 } else { 517 if (mGenreRows[i] == null || mRowsAdapter.indexOf(mGenreRows[i]) < 0) { 518 mGenreRows[i] = new ListRow(new HeaderItem(mGenreLabels.get(i)), adapter); 519 mRowsAdapter.add(visibleRowsCount, mGenreRows[i]); 520 } 521 visibleRowsCount++; 522 } 523 } 524 } 525 } 526 needToShowScheduledRecording(ScheduledRecording recording)527 private boolean needToShowScheduledRecording(ScheduledRecording recording) { 528 int state = recording.getState(); 529 return state == ScheduledRecording.STATE_RECORDING_IN_PROGRESS 530 || state == ScheduledRecording.STATE_RECORDING_NOT_STARTED; 531 } 532 updateLatestRecordedProgram(SeriesRecording seriesRecording)533 private void updateLatestRecordedProgram(SeriesRecording seriesRecording) { 534 RecordedProgram latestProgram = null; 535 for (RecordedProgram program : 536 mDvrDataManager.getRecordedPrograms(seriesRecording.getId())) { 537 if (latestProgram == null || RecordedProgram 538 .START_TIME_THEN_ID_COMPARATOR.compare(latestProgram, program) < 0) { 539 latestProgram = program; 540 } 541 } 542 mSeriesId2LatestProgram.put(seriesRecording.getSeriesId(), latestProgram); 543 } 544 545 private class ScheduleAdapter extends SortedArrayAdapter<Object> { ScheduleAdapter(int maxItemCount)546 ScheduleAdapter(int maxItemCount) { 547 super(mPresenterSelector, SCHEDULE_COMPARATOR, maxItemCount); 548 } 549 550 @Override getId(Object item)551 public long getId(Object item) { 552 if (item instanceof ScheduledRecording) { 553 return ((ScheduledRecording) item).getId(); 554 } else { 555 return -1; 556 } 557 } 558 } 559 560 private class SeriesAdapter extends SortedArrayAdapter<SeriesRecording> { SeriesAdapter()561 SeriesAdapter() { 562 super(mPresenterSelector, new Comparator<SeriesRecording>() { 563 @Override 564 public int compare(SeriesRecording lhs, SeriesRecording rhs) { 565 if (lhs.isStopped() && !rhs.isStopped()) { 566 return 1; 567 } else if (!lhs.isStopped() && rhs.isStopped()) { 568 return -1; 569 } 570 return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs); 571 } 572 }); 573 } 574 575 @Override getId(SeriesRecording item)576 public long getId(SeriesRecording item) { 577 return item.getId(); 578 } 579 } 580 581 private class RecordedProgramAdapter extends SortedArrayAdapter<Object> { RecordedProgramAdapter()582 RecordedProgramAdapter() { 583 this(Integer.MAX_VALUE); 584 } 585 RecordedProgramAdapter(int maxItemCount)586 RecordedProgramAdapter(int maxItemCount) { 587 super(mPresenterSelector, RECORDED_PROGRAM_COMPARATOR, maxItemCount); 588 } 589 590 @Override getId(Object item)591 public long getId(Object item) { 592 if (item instanceof SeriesRecording) { 593 return ((SeriesRecording) item).getId(); 594 } else if (item instanceof RecordedProgram) { 595 return ((RecordedProgram) item).getId(); 596 } else { 597 return -1; 598 } 599 } 600 } 601 }